Skip to main content
Glama
kraenhansen

nemlig-mcp

by kraenhansen

nemlig-mcp

Unofficial local MCP for nemlig.com, danish delivery service

Built as a thin layer over nemlig_cli, which already solves the hard parts: the three-step XSRF/bearer/login flow, the cookie-backed session, the two different API hosts, and the GetAsJson endpoints. This package adds a token-refresh loop, a privacy filter, and a curated tool surface.

Tools

Tool

Writes?

Description

search_products

no

Search the catalogue

get_product_details

no

Nutrition, allergens, attributes

get_basket

no

Current basket contents

set_basket_quantity

yes

Set units of a product in the basket (0 removes it)

set_basket_quantities

yes

Same, for a list of products, applied one by one with retries

get_order_history

no

Previous orders

get_order_details

no

Line items of one past order

Related MCP server: MCP Picnic

What this server deliberately cannot do

It cannot place an order or read your payment cards. Nemlig's API exposes POST /webapi/Order/PlaceOrderLoggedIn (which charges a saved card) and GET /webapi/Checkout/GetCreditCards. nemlig_cli implements neither, and this server does not add them.

The tool surface is an allowlist, not a filtered view of the whole API, so an endpoint cannot become reachable by accident. A test asserts this (tests/test_server.py). The two set_basket_quantit* tools are the only ones that change state, and every change they make is reversible on the website.

Both are annotated destructive_hint=True, because their quantity is absolute rather than a delta: lowering it discards units already in the basket, and 0 removes the line. Clients use that hint to decide whether to confirm with the user, so understating it would be the dangerous direction to be wrong in.

Batching

set_basket_quantities takes a list of {product_id, quantity} and applies it sequentially, so filling a basket from a recipe or a past order is one tool call rather than fifteen. A failing item is retried up to three times with a growing backoff, but only for failures another attempt could actually fix — a timeout, a 429 or a 5xx. A request nemlig rejected outright is not retried, because it would be rejected again and only delay the rest of the batch.

A failing item is recorded and skipped rather than aborting the run: the basket has already been half-changed by that point, so the useful thing to return is what happened to every item plus the resulting basket. The tool reports both, and reports the basket as null with a basket_error if even the final read fails, rather than throwing away the per-item results.

Because quantities are absolute, a product may appear only once in the list; two lines for one product would silently mean "last one wins" rather than the sum a caller likely intended, so that is rejected up front.

Privacy

Basket and order responses embed the account holder's name, street address and phone number. Everything a tool returns lands in the model's context and in the conversation transcript, so those fields are replaced with a marker before they leave the server. See privacy.py.

Setup

1. Install

git clone https://github.com/kraenhansen/nemlig-mcp.git
cd nemlig-mcp
uv sync

Requires uv and Python 3.11+. nemlig_cli is pulled from git automatically — no separate checkout needed.

2. Provide credentials

Two options. Prefer the config file — an MCP config with a password in it is easy to commit by accident.

mkdir -p ~/.config/nemlig
cat > ~/.config/nemlig/login.json <<'EOF'
{"username": "you@example.com", "password": "your-password"}
EOF
chmod 600 ~/.config/nemlig/login.json

This is the same file the CLI uses, so both share one login. Alternatively set NEMLIG_USER / NEMLIG_PASS in the environment, which takes precedence.

3. Register the server

claude mcp add nemlig -- uv --directory ~/code/nemlig-mcp run nemlig-mcp

Or add it to .mcp.json (project-local) or ~/.claude.json (global). Use an absolute path — ~ is not expanded inside args:

{
  "mcpServers": {
    "nemlig": {
      "command": "uv",
      "args": ["--directory", "/Users/you/code/nemlig-mcp", "run", "nemlig-mcp"]
    }
  }
}

If you would rather not use the config file, add credentials here instead — and make sure the file is gitignored:

      "env": { "NEMLIG_USER": "you@example.com", "NEMLIG_PASS": "..." }

4. Verify

uv run pytest                        # 20 tests, no network, no credentials
uv run python tests/smoke_stdio.py   # spawns the server, lists its tools

Then ask Claude something like "search nemlig for kaffebønner". The first call logs in, which takes a second or two; tokens are cached for 240s after that.

Troubleshooting:

Symptom

Cause

No nemlig.com credentials found

Step 2 not done, or the config file is not valid JSON

ModuleNotFoundError: nemlig_cli

uv sync did not complete — rerun it

Tool calls fail with HTTPError

Wrong username/password — nemlig returns 401 from the login endpoint

Relationship to the upstream CLI

Building this surfaced two problems in nemlig_cli, both fixed upstream in eisbaw/nemlig_cli#3:

  1. login() could hang forever. The progress spinner was started before the request sequence but only stopped on the success path, on a non-daemon thread — so a wrong password left the process spinning instead of raising.

  2. Progress was written to stdout, which on a stdio MCP server is the JSON-RPC transport. It now renders to stderr, and only when stderr is a tty.

Because of that fix, nemlig-cli is pinned to a commit that includes it. The quiet() wrapper in _compat.py is kept anyway: a single stray print() anywhere in a dependency would corrupt the protocol stream, and that is cheap insurance against a failure mode this severe.

Development

uv run pytest                        # 20 tests, no network, no credentials
uv run python tests/smoke_stdio.py   # end-to-end over stdio

To work against a local checkout of the CLI, uncomment the [tool.uv.sources] block in pyproject.toml and clone nemlig_cli as a sibling directory.

Disclaimer

Not affiliated with nemlig.com. Uses a private API that may change or break, and automated access may conflict with their terms of service.

License

MIT

Available Tools

6 tools
get_basketA
Read-only

Show the current shopping basket with line items and prices.

Delivery and invoice addresses are redacted.

ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Beyond the readOnlyHint annotation, the description adds a valuable behavioral detail: delivery and invoice addresses are redacted. This is additional context not present in annotations or schema, and it does not contradict any annotation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is two concise sentences: the first clearly states the tool's purpose, the second adds a key behavioral caveat. Every word earns its place, and the most important information is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a simple, no-parameter, read-only tool with an output schema. The description covers the essential purpose and discloses the redaction behavior, which is sufficient for an agent to invoke it correctly without further elaboration.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The tool has zero parameters, so the schema provides complete coverage (100% by vacuity). The description has no need to explain parameter semantics, and the baseline of 4 is appropriate.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states a specific verb 'Show' on a specific resource 'current shopping basket' and details the included content (line items and prices). It clearly distinguishes this from sibling tools such as get_order_details (orders vs. basket) and set_basket_quantity (mutation).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage for viewing the user's current basket but does not explicitly state when to use this tool over alternatives or note exclusions. No external comparison is provided, leaving the agent to infer the tool's role purely from its name and context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_order_detailsA
Read-only

Fetch the line items of one past order.

Args: order_id: Numeric order Id from get_order_history (not OrderNumber).

ParametersJSON Schema
NameRequiredDescriptionDefault
order_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, and the description adds the 'past order' scope, clarifying this is not a current-basket operation. The note about using the numeric ID from get_order_history (not OrderNumber) provides practical behavioral context beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Two short sentences plus a single-argument doc; every sentence earns its place. No redundancy or filler, with the key purpose stated first.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter read-only tool with an output schema, the description covers everything needed: purpose, input source, and the ID type. Return values are covered by the output schema, so no additional explanation is required.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates: it explains that order_id is the numeric internal ID (not OrderNumber) from get_order_history. This is crucial for correct invocation and adds meaning the schema alone lacks.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Description opens with 'Fetch the line items of one past order' – a specific verb ('fetch'), specific resource ('line items of one past order'), and clearly differentiates from siblings like get_order_history (which lists orders) and get_basket (current cart).

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

States that order_id must be the numeric ID from get_order_history, explicitly excluding OrderNumber. This implies the workflow: call get_order_history first, then use the returned ID here. It doesn't name alternatives explicitly but provides clear contextual guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_order_historyA
Read-only

List previous orders, most recent first.

Args: skip: Number of orders to skip, for pagination. take: Number of orders to return (1-50).

Returns: Orders with Id, OrderNumber, Total and delivery window. Use Id with get_order_details. Addresses are redacted.

ParametersJSON Schema
NameRequiredDescriptionDefault
skipNo
takeNo

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint and openWorldHint, so the safe-read nature is covered. The description adds meaningful behavioral context: addresses are redacted, returns include delivery window, and pagination semantics are explained, which goes beyond the structured annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and well-structured with labeled Args and Returns sections. No redundant content; every sentence adds practical information.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a list endpoint with a simple schema and an output schema, the description covers ordering, pagination, redaction, and returned key fields. It also provides guidance for the natural next step (get_order_details), making it contextually complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Despite 0% schema description coverage, the description fully explains both parameters: skip as number to skip and take as count with a range constraint (1-50). This is essential semantic detail that the schema's type/default fields do not convey.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

Clearly identifies the tool as listing previous orders in reverse chronological order. The verb 'List' and resource 'orders' are specific, and the description distinguishes it from get_order_details by implying this is the overview tool.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly directs users to use the returned Id with get_order_details, indicating when to chain to the detail tool. However, it does not enumerate situations where this tool should be avoided or alternatives for filtering/searching, though sibling tools are mostly product-focused.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_product_detailsA
Read-only

Fetch full details for one product, including nutrition and allergens.

Args: product_id: Product Id from search_products, e.g. "5070417".

ParametersJSON Schema
NameRequiredDescriptionDefault
product_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A4.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations already declare readOnlyHint=true, so the safe-read behavior is covered. The description adds that it returns nutrition and allergens, which is a minor behavioral detail, but it does not disclose error handling, rate limits, or other operational behavior. No contradiction with annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loaded with the main purpose, followed by a clearly formatted args section. Every sentence serves a purpose, with no redundant content.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

This is a simple one-parameter read tool with an output schema, so return values are already covered by the schema. The description, annotations, and sibling list provide complete context for selecting and invoking the tool correctly.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does so effectively by explaining that product_id is 'Product Id from search_products' and providing an example ('5070417'), which adds meaningful guidance beyond the bare schema type declaration.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description uses the specific verb 'Fetch' with a clear resource ('full details for one product'), and explicitly mentions nutrition and allergens. It distinguishes itself from siblings like search_products (which searches) and order tools by focusing on a single product's details.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies usage after search_products by stating product_id comes from search_products, providing clear context. It does not explicitly mention when not to use or alternatives, but the sibling names and the phrase 'one product' offer sufficient guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_productsA
Read-only

Search the nemlig.com catalogue.

Args: query: Search term. Danish terms match best, e.g. "kaffebønner", "mælk". limit: Maximum number of products to return (1-50).

Returns: Matching products with Id, Name, Brand, Price and availability. Use the Id with get_product_details or set_basket_quantity.

ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes

Output Schema

ParametersJSON Schema
NameRequiredDescription
resultYes

TDQS

A4.6/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

Annotations declare readOnlyHint=true and openWorldHint=true, so the safe-read behavior is already known. The description adds value by specifying the return fields (Id, Name, Brand, Price, availability) and the limit constraint (1-50). This is useful behavioral context beyond the annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is well-structured with Args and Returns sections. Every sentence adds value: search query hints, parameter explanation, and return usage. No fluff or repetition.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the tool's simple nature (2 params, output schema exists, no nested objects), the description is complete. It covers the search behavior, parameter semantics, return format, and downstream usage (get_product_details/set_basket_quantity). Users know exactly what to expect and how to proceed.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description provides detailed semantics for both parameters: query is the search term (with language hint), and limit is the maximum number of results (with range 1-50). This fully compensates for the minimal schema and adds meaning beyond the type declarations.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states the tool's purpose: 'Search the nemlig.com catalogue.' This is a specific verb (search) with a resource (catalogue) and differentiates from siblings like get_product_details or set_basket_quantity, which are for retrieval or mutation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Provides useful guidance on when to use this tool: as the entry point for searching, with a note that Danish terms work best. It also directs the user to use the returned Id with get_product_details or set_basket_quantity, implying the search is a precursor to those tools. No explicit exclusions, but clear context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

set_basket_quantityA
Destructive

Set how many units of a product the basket should contain.

The quantity is absolute, not a delta. It is the number of units to end up with, so setting 2 on a line that currently holds 5 removes 3 of them, and setting 0 removes the product entirely. To add to a line that may already exist, read its current Quantity with get_basket and pass the new total.

This changes the user's real basket on nemlig.com. It does not place an order or charge anything -- the user completes checkout themselves.

Args: product_id: Product Id from search_products, e.g. "5070417". quantity: Units the basket should end up with. 0 removes the product.

Returns: The updated basket, with addresses redacted.

ParametersJSON Schema
NameRequiredDescriptionDefault
quantityNo
product_idYes

Output Schema

ParametersJSON Schema
NameRequiredDescription

No output parameters

TDQS

A5/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

The description adds significant behavioral context beyond the annotations: the quantity is absolute rather than a delta, setting 0 removes the product, it changes the user's real basket, and the returned basket has addresses redacted. These details are not present in the structured annotations and are crucial for safe invocation.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the core purpose, then provides essential semantics and side effects in a clear paragraph style, followed by an Args/Returns section. Every sentence serves a purpose, with no repetition of schema defaults or redundant fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-parameter tool with output schema, the description covers all necessary context: absolute quantity semantics, how to calculate a delta, the real-world side effect, what it does not do, and what the return value contains. No significant information gap remains.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With 0% schema description coverage, the description fully compensates by explaining each parameter: product_id is 'Product Id from search_products' with an example, and quantity is 'Units the basket should end up with. 0 removes the product.' This adds meaning beyond the raw schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states exactly what the tool does: 'Set how many units of a product the basket should contain.' It uses a specific verb and resource, and clearly distinguishes itself from siblings like get_basket and search_products by describing a mutation operation.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It explicitly explains when to use this tool versus alternatives: 'To add to a line that may already exist, read its current Quantity with get_basket and pass the new total.' It also clarifies what it does not do ('does not place an order or charge anything'), providing clear usage context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

TDQS

A4.7/5.0
Disambiguation5/5

Each tool targets a distinct resource and action: search vs. details, basket vs. order, and list vs. detail vs. modify. No two tools overlap in purpose.

Naming Consistency5/5

All tools follow the consistent verb_noun pattern in snake_case: search_products, get_product_details, get_basket, get_order_details, set_basket_quantity, get_order_history.

Tool Count5/5

Six tools is well-scoped for a grocery shopping assistant, covering product discovery, basket management, and order history without unnecessary bloat.

Completeness5/5

The domain is fully covered: product search/detail, basket read/update (including removal via quantity 0), and order list/detail. Checkout is intentionally omitted as the user completes it manually.

Maintenance

ActivityMaintained
ResponsivenessSyncing

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Connectors

Related MCP Servers

  • F
    license
    B
    quality
    C
    maintenance
    Enables AI assistants to interact with Mathem.se, a Swedish online grocery store, allowing users to search for ingredients, add items to their shopping basket, and manage recipes through natural language.
    4
    2
  • A
    license
    Not graded
    quality
    A
    maintenance
    Enables AI assistants to interact with Picnic online supermarket for grocery shopping, meal planning, cart management, delivery tracking, and budget-conscious shopping in Netherlands and Germany.
    248
    95
    MIT
  • A
    license
    Not graded
    quality
    Not graded
    maintenance
    Enables agentic grocery shopping on Oda (Norway) and Mathem (Sweden) platforms through an MCP-compatible interface. Users can search for products, manage their shopping cart, and access order history using natural language commands.
    1

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/kraenhansen/nemlig-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server