Skip to main content
Glama
orjahren

Nettskjema MCP Server

by orjahren

Server Configuration

Describes the environment variables required to run the server.

NameRequiredDescriptionDefault
NETTSKJEMA_CLIENT_IDYesYour Nettskjema API client ID
NETTSKJEMA_CLIENT_SECRETYesYour Nettskjema API client secret

Instructions

Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.

This server publishes no instructions, or was last inspected before Glama recorded them.

Capabilities

Features and capabilities supported by this server

Protocol revision2025-11-25

CapabilityDetails
tools
{
  "listChanged": true
}
logging
{}
prompts
{
  "listChanged": false
}
resources
{
  "subscribe": false,
  "listChanged": false
}
extensions
{
  "io.modelcontextprotocol/ui": {}
}
experimental
{}

Tools

Functions exposed to the LLM to take actions

NameDescription
activate_tsdB

Activate TSD (Services for Sensitive Data) for a form.

The token parameter is a credential that authorizes activating TSD for the named project. It must be kept as secure as the OAuth2 access token and is never logged or included in error messages.

The API returns 200 with no response body on success.

activate_educloudA

Activate Educloud for a form.

The token parameter is a credential that authorizes activating Educloud for the named project. It must be kept as secure as the OAuth2 access token and is never logged or included in error messages.

The API returns 200 with no response body on success.

upload_imageB

Upload an image to the API, returning a response with string keys and values.

The entire file is read into memory, base64-encoded, and sent as a JSON string in the request body, per the published OpenAPI schema ({"type": "string", "format": "byte"} under application/json). The filename is derived from the file itself and sent as a query parameter.

The body format is most likely a Spring Boot serialisation artefact where a byte[] parameter was rendered as format: byte. The actual server may expect raw bytes; whether it accepts the JSON string can only be settled end-to-end.

get_imageA

Download an image to a file, written to disk.

The image is streamed to disk and never inlined. The returned path is absolute and tilde-expanded. An existing file at the output path is silently overwritten, and its mode is preserved. A new file (whether from output_path or a temp file) is created with mode 0600 (readable only by the current user).

When output_path is omitted, a temporary file is created in the system temp directory with a suffix derived from the response's media type. The temporary file persists after this call returns and is left on disk for the caller to use or delete.

The spec declares the response as / (any media type), so the actual content type is reported as received.

get_form_answersA

Stream all answers for a form, as newline-delimited JSON.

This exports every answer on the form, one JSON object per line (NDJSON), so a large form's answers are never held whole in memory. Filter by element_ids to export only specific questions. The result is capped at max_rows (default 1000) to bound context size: check the returned truncated field to see if more data exists. When truncated is true, the reason field distinguishes a hit row cap ("rowCap") from a connection that closed mid-line ("incompleteFinalLine").

get_answer_attachmentA

Download an answer attachment to a file, written to disk.

The attachment is streamed to disk and never inlined: a multi-megabyte audio recording as base64 is useless to a model. The returned path is absolute and tilde-expanded. An existing file at the output path is silently overwritten, and its mode is preserved. A new file (whether from output_path or a temp file) is created with mode 0600 (readable only by the current user).

When output_path is omitted, a temporary file is created in the system temp directory with a suffix derived from the response's media type. The temporary file persists after this call returns and is left on disk for the caller to use or delete.

upload_submission_attachmentA

Upload an attachment to a submission question, optionally requesting transcription.

The entire file is read into memory before uploading. The filename and fileSize are derived from the file itself rather than accepted as parameters, so they cannot drift out of sync. An audio recording awaiting transcription can be tens of megabytes; the whole file is held in RAM during the upload.

The attachment token comes from the attachmentToken field in the response body of POST /form/{formId}/submission. It authorizes writing to someone else's submission and must be kept as secure as the access token itself.

The spec does not document what happens when the named questionId already has an attachment. An upload may silently replace a respondent's file.

Transcription options apply only when the uploaded file is audio or video. The six supported models are: model_auto, nb_whisper_large_semantic, nb_whisper_large_verbatim, openai_whisper_large_v3, openai_whisper_large_v2, and KBLab-large. The language parameter accepts 100+ language codes; passing an unknown code raises BadRequestError naming the allowed values. The diarize flag requests speaker identification, and translate_to_english requests translation to English after transcription.

get_attachment_transcriptionA

Get the transcription of an attachment in JSON, plain text, or WebVTT format.

The attachment must have been uploaded with transcription options for this to return meaningful results. A file uploaded without transcription, or a file that is not audio or video, has no transcription to retrieve.

For txt and vtt formats, the result is capped at max_chars (default 1,000,000) to bound context size. Check the truncated field to see if the full text was cut. The caller may raise this limit arbitrarily by passing a larger value.

retranscribe_attachmentA

Retranscribe an existing attachment with new transcription options.

The API overwrites the existing transcription. The attachment must be audio or video; retranscribing a non-media file or a file that was never transcribed has undefined effect.

The six supported models are: model_auto, nb_whisper_large_semantic, nb_whisper_large_verbatim, openai_whisper_large_v3, openai_whisper_large_v2, and KBLab-large. The language parameter accepts 100+ language codes; passing an unknown code raises BadRequestError naming the allowed values. The diarize flag requests speaker identification, and translate_to_english requests translation to English after transcription.

get_form_elementsA

Get the form's elements: its questions, headings, images and page breaks.

Use this before any element edit. The only write endpoint replaces the whole list, so the array returned here is the starting point for a modified list.

replace_form_elementsA

Replace the form's ENTIRE element list. Every element you leave out is DELETED.

This is not an "add an element" tool. Whatever you send becomes the form's complete content: any question, heading, image or page break that is not in elements is removed from the form. To add or change anything, call get_form_elements first, modify the array it returns, and send all of it back — or call update_form_element, which does that read-modify-write for one element. Sending a one-element list to a ten-element form deletes nine elements.

Nothing locks the form in between: an edit made by someone else after your get_form_elements call is overwritten by this one without warning. The endpoint offers no version field, ETag or conditional header to detect that.

Each element is checked against the vendored ElementDTO schema before the request is sent: elementType is required on every element and must be one of the known types, and every field the schema declares is checked for type and range. A field the schema does not declare is passed through to the API rather than rejected here, so an element read back from a newer API than the vendored snapshot can still be written back unchanged.

update_form_elementA

Change one element, by rewriting the form's ENTIRE element list.

The API has no single-element update, so this tool reads every element, applies changes to the one whose elementId matches, and writes all of them back through the same whole-list PUT that replace_form_elements uses. The other elements are sent back exactly as they were read — but they are sent, so an edit someone else makes between the read and the write is silently overwritten. The operation is NOT atomic, and the endpoint offers no version field, ETag or conditional header that would detect the conflict.

changes is merged shallowly, one level deep: each key replaces that key's value on the element outright. Nested lists are NOT merged item by item — passing answerOptions replaces the element's whole answer-option list, so to edit one option you must send all of them, each keeping its answerOptionId, in the order you want. The same goes for subElements and validationScripts. Fetch the element with get_form_elements first if you do not already have them.

If no element in the form has the given element_id, nothing is written: a PUT of the unchanged list would report success while changing nothing. For the same reason changes may not be empty, and may not carry elementId — changing an element's own id does not move the edit to another element, it asks the API to replace this one with a different element.

export_form_csvA

Export a form's submission data as CSV.

The spec's summary describes the response as a CSV text file with semicolon (;) as the delimiter. When output_path is provided, the file is written to disk (mode 0600 for a new file) and the result reports the path, byte count and media type. When output_path is omitted, the text is returned as JSON with a size signal and truncation flag. The result is capped at max_chars (default 1,000,000) to bound context size: check the truncated field to see if the full export was cut.

export_form_excelA

Export a form's submission data to a file, written to disk.

The spec documents this route's response as an Excel file. The file is streamed to disk and never inlined: a multi-megabyte spreadsheet as base64 is useless to a model. The returned path is absolute and tilde-expanded. An existing file at the output path is silently overwritten, and its mode is preserved. A new file (whether from output_path or a temp file) is created with mode 0600 (readable only by the current user).

When output_path is omitted, a temporary file is created in the system temp directory with a suffix derived from the response's media type. The temporary file persists after this call returns and is left on disk for the caller to use or delete.

get_spss_syntaxA

Get SPSS syntax for a form's submission data.

The spec documents this route's response as an SPSS syntax file. The syntax is returned as JSON with a size signal and truncation flag. The result is capped at max_chars (default 1,000,000) to bound context size: check the truncated field to see if the full syntax was cut.

upload_codebookA

Upload a codebook mapping form elements to external identifiers.

The spec does not document whether omitted entries are preserved or deleted. Send the complete mapping you want the form to have: include every element, sub-element and answer option whose external id you want to keep or set. Omitting an entry has undefined effect and may delete that mapping.

Each map is validated against the CodebookDTO schema before the request is sent. All three maps are required by the schema: send an empty dict for any map you do not need to populate.

list_my_formsB

List all forms owned by the authenticated API client.

Returns: An array of form summary objects as JSON. Each object includes formId, title, open (boolean), modifiedDate, lastSubmissionDate, numberOfDeliveredSubmissions, deliveryDestination, and myFormsFormListingGroup.

lookup_form_by_titleB

Look up a form by its short title (the URL-safe identifier).

get_form_infoB

Get operational metadata about a form: submission counts, open/closed status, and edit permissions.

get_form_settingsB

Get the form's configuration: all 51 settings controlling form behavior and permissions.

get_form_settings_validationC

Get validation errors or constraints for the form's settings.

get_form_definitionC

Get the form's structure and creation history: the elements array and authorship metadata.

list_forms_collecting_personal_dataC

List forms that collect personal data, with optional filters.

create_formA

Create a new form with the given settings and optional elements.

The six required settings are taken as named parameters so a model cannot omit them. Additional settings can be provided via extra_settings. If a required setting appears in extra_settings, the named parameter takes precedence.

delete_formsA

Delete one or more forms by their IDs.

Deleted forms are moved to the trash and can be restored for a limited time. This operation is destructive and cannot be undone after the trash retention period expires.

update_form_settingsA

Update one or more settings on an existing form.

This endpoint accepts partial updates: only the settings included in the settings dict will be updated. A 207 Multi-Status response means some settings were applied and others were rejected; this is reported as an error listing the failed settings.

The settings dict is validated as a partial FormSettingsDTO before the request is sent. Required-field constraints are relaxed for partial updates, but unknown keys are rejected locally to catch typos and flag fields that are newer than the vendored spec snapshot.

remind_all_pending_invitationsA

Send reminder emails to every pending invitation on a form.

This operation is all-or-nothing: every sent invitation on the form receives a reminder. There is no endpoint to remind a subset of invitations. Whether it also reaches invitations created through the newer batch interface is not documented anywhere; they may share the same storage, but that is an inference and not something the API states.

Creating, viewing and deleting invitations are not available through this MCP server. Those operations are public Nettskjema features — supported, documented, production-ready — but are managed through the web frontend instead. Use the form management interface to handle invitations: https://nettskjema.no/user/form/{formId}/collect-responses/invited/create and https://nettskjema.no/user/form/{formId}/collect-responses/invited/overview

get_user_infoB

Get the Nettskjema user record for the authenticated API client.

Returns: The user record as JSON.

check_auth_statusA

Report which Nettskjema credentials and URLs this server is configured with.

Makes no request, so it still answers when authentication is the thing that is broken. The client secret is never included: only whether it is set.

Returns: A JSON object describing the current configuration, the names of any missing environment variables, and whether the server is ready to call the API.

get_submissionB

Get a single submission with its answers and metadata.

list_my_submissionsB

List all submissions made by the authenticated API client.

Returns: An array of submission summary objects as JSON. Each object includes submissionId, form (with title), createdDate, modifiedDate, and delivered (boolean).

get_submission_pdfA

Download a submission as a PDF file.

The PDF is written to disk, not returned inline: a multi-megabyte PDF as base64 is not useful to a model. The returned path is absolute and tilde-expanded. An existing file at the output path is silently overwritten, and its mode is preserved. A new file (whether from output_path or a temp file) is created with mode 0600 (readable only by the current user).

When output_path is omitted, a temporary file is created in the system temp directory with a .pdf suffix. The temporary file persists after this call returns and is left on disk for the caller to use or delete.

get_submission_metadataA

Stream all submission metadata for a form, as newline-delimited JSON.

Returns metadata without answers: use this for bulk export of submission-level fields or to find which submissions exist before fetching their answers. This streams NDJSON (one JSON object per line), so a large form's metadata is never held whole in memory. The result is capped at max_rows to bound context size: check the returned truncated field to see if more data exists.

Distinct from get_postponed_submission_metadata, which returns postponed submission metadata only. The relationship between the two result sets (whether this endpoint includes or excludes postponed submissions) is not specified by the API documentation.

get_postponed_submission_metadataA

Stream postponed submission metadata for a form, as newline-delimited JSON.

This endpoint returns the same metadata structure as get_submission_metadata, but only for postponed submissions. The result is streamed as NDJSON and capped at max_rows to bound context size.

Distinct from get_submission_metadata: this endpoint returns postponed submission metadata only, while that one returns all submission metadata for the form.

submit_formA

Deliver a response to a form, as the authenticated API client.

This submits a real answer to a real form: it adds a row to the form's collected data, and the form's owner sees it alongside human responses. There is no draft or preview mode here. Use submit_form_for_invitation instead when answering on behalf of a specific invitation.

Each answer is checked against the vendored ClientAnswerDTO schema before the request is sent, so a bad enum value or a mistyped field fails without reaching the form. A field the snapshot does not declare is passed through to the API, which has the final say.

submit_form_for_invitationA

Deliver a response to the form a given invitation was issued for.

Same delivery as submit_form, addressed by invitation rather than by form: the response is recorded against that invitation, so the form's owner can see which invited respondent answered. The invitation is identified by its own numeric id, not by the form's. This submits a real answer to a real form; there is no draft or preview mode.

Each answer is checked against the vendored ClientAnswerDTO schema before the request is sent. A field the snapshot does not declare is passed through to the API, which has the final say.

delete_submissionsA

Delete ONLY the listed responses to a form. Other responses are left alone.

This is the scoped delete, and the one to use whenever specific responses are meant: it removes exactly the submissions whose ids are given, and nothing else. Do not confuse it with delete_all_submissions, which deletes every response the form has and takes no id list. Deleted responses are gone; the API offers no undo for them.

Get submission ids from get_submission_metadata or list_my_submissions. An empty list is refused rather than sent, because what the API would do with an empty body here is not specified and the cost of guessing wrong is a form's data.

delete_all_submissionsA

Delete EVERY response the form has ever collected. This CANNOT be undone.

There is no id list and no partial form of this call: the whole dataset goes, for every respondent, and the API offers no undo and no trash can for submissions. Confirm with the user before calling it. To remove particular responses, use delete_submissions, which takes submission ids and leaves the rest of the form's data in place.

The spec's plural operation accepts an optional array body, but documents the operation as deleting all submissions and never says the ids narrow it. This tool therefore sends no body and offers no id parameter, rather than a parameter that looks like it limits the damage and may not.

Prompts

Interactive templates invoked by user choice

NameDescription

No prompts

Resources

Contextual data attached and managed by the client

NameDescription

No resources

TDQS

A3.7/5.0

Scored across 38 tools

Disambiguation5/5

Despite 38 tools, purposes are sharply distinct and descriptions actively call out near-neighbors (e.g. delete_submissions vs delete_all_submissions, get_submission_metadata vs get_postponed_submission_metadata, replace_form_elements vs update_form_element). The cluster of get_form_info/settings/settings_validation/definition tools targets distinct facets (metadata, config, validation, structure) and is well differentiated in prose.

Naming Consistency5/5

Near-perfect snake_case verb_noun convention throughout: get_*, list_*, create_*, update_*, delete_*, upload_*, export_*, submit_*, activate_*. Pluralization and scoping (delete_forms, delete_submissions, delete_all_submissions) is used meaningfully and consistently.

Tool Count3/5

38 tools is heavy and exceeds the comfortable 15-tool band, spanning forms, elements, settings, submissions, attachments, transcription, exports, invitations, users, auth, and codebook. The domain is genuinely broad and most tools map to distinct endpoints, but the surface is large enough that consolidation (e.g. the several get_form_* readers) would help.

Completeness4/5

Core lifecycle is well covered: form create/read/delete, element read/replace/update, settings read/update/validate, submission create/get/list/bulk-export/delete, attachment upload/download/transcribe, and multiple export formats. Gaps remain in invitation management (only remind_all_pending_invitations; create/view/delete are acknowledged as unavailable) and codebook is upload-only with no update/delete or form rename.

Maintenance

ActivityMaintained
ResponsivenessNo issues