# OpenAlex MCP Server: 200M Papers & Authors for AI Agents (`dltik/mcp-server-openalex`) Actor

MCP server exposing 8 tools to query OpenAlex (200M+ scholarly works, authors, institutions, concepts, sources). HTTP-only, no API key. $0.005/tool-call.

- **URL**: https://apify.com/dltik/mcp-server-openalex.md
- **Developed by:** [Walid](https://apify.com/dltik) (community)
- **Categories:** AI, Developer tools, Business
- **Stats:** 2 total users, 1 monthly users, 100.0% runs succeeded, 0 bookmarks
- **User rating**: No ratings yet

## Pricing

from $0.00005 / actor start

This Actor is paid per event. You are not charged for the Apify platform usage, but only a fixed price for specific events.

Learn more: https://docs.apify.com/platform/actors/running/actors-in-store#pay-per-event

## What's an Apify Actor?

Actors are a software tools running on the Apify platform, for all kinds of web data extraction and automation use cases.
In Batch mode, an Actor accepts a well-defined JSON input, performs an action which can take anything from a few seconds to a few hours,
and optionally produces a well-defined JSON output, datasets with results, or files in key-value store.
In Standby mode, an Actor provides a web server which can be used as a website, API, or an MCP server.
Actors are written with capital "A".

## How to integrate an Actor?

If asked about integration, you help developers integrate Actors into their projects.
You adapt to their stack and deliver integrations that are safe, well-documented, and production-ready.
The best way to integrate Actors is as follows.

In JavaScript/TypeScript projects, use official [JavaScript/TypeScript client](https://docs.apify.com/api/client/js.md):

```bash
npm install apify-client
```

In Python projects, use official [Python client library](https://docs.apify.com/api/client/python.md):

```bash
pip install apify-client
```

In shell scripts, use [Apify CLI](https://docs.apify.com/cli/docs.md):

````bash
# MacOS / Linux
curl -fsSL https://apify.com/install-cli.sh | bash
# Windows
irm https://apify.com/install-cli.ps1 | iex
```bash

In AI frameworks, you might use the [Apify MCP server](https://docs.apify.com/platform/integrations/mcp.md).

If your project is in a different language, use the [REST API](https://docs.apify.com/api/v2.md).

For usage examples, see the [API](#api) section below.

For more details, see Apify documentation as [Markdown index](https://docs.apify.com/llms.txt) and [Markdown full-text](https://docs.apify.com/llms-full.txt).


# README

## OpenAlex MCP Server: 200M Academic Papers, Authors, Institutions for AI Agents

> An Apify-hosted MCP server exposing **8 tools** to query [OpenAlex](https://openalex.org) — the open catalogue of 200M+ scholarly works, authors, institutions, sources, and concepts. HTTP-only, no API key. **$0.005 per tool-call**.

⭐ **Bookmark this OpenAlex MCP Server** — Apify ranks actors by bookmarks, so it directly helps the visibility of this actor on the Apify Store.
[→ Bookmark it on the Apify Store](https://apify.com/dltik/mcp-server-openalex)

### What can this OpenAlex MCP Server do?

- 📚 **Search 200M+ scholarly works** — papers, books, datasets across every discipline.
- 👩‍🔬 **Find authors with h-index, ORCID, institution** — full bibliometric profiles.
- 🏛️ **Resolve institutions & ROR IDs** — universities, hospitals, research orgs in 200+ countries.
- 🧠 **Query the OpenAlex concept taxonomy** — Wikidata-linked research topics, levels 0-5.
- 📰 **Discover sources** — journals, conferences, repositories, OA status, DOAJ flag.
- 🤖 **MCP-shaped for AI agents** — call from Claude, Cursor, Continue with one tool dispatcher.

### What data can you extract with the OpenAlex MCP Server?

| Field | Description |
|---|---|
| `openalex_id` | Stable OpenAlex identifier (W… / A… / I… / S… / C…) |
| `doi` | DOI of the work (when available) |
| `title` | Title of the work / author / institution / source |
| `publication_year` | Year published |
| `publication_date` | ISO publication date |
| `cited_by_count` | Total citation count |
| `authorships` | Full nested authorship objects (raw OpenAlex) |
| `authors` | Flat list of author display names |
| `primary_location` / `primary_source` | Hosting journal / repo info |
| `open_access` | `is_oa`, `oa_status`, `oa_url` (Unpaywall-style) |
| `concepts` | Wikidata-linked research topics with score |
| `type` | Work type (article, book, dataset, …) |
| `language` | ISO 639 code |
| `referenced_works_count` | Number of references the work cites |
| `abstract_inverted_index` | Raw inverted index (word → positions) |
| `abstract` | Reconstructed plain-text abstract (when `reconstruct_abstract: true`) |
| `bookmark_cta` | Last record — bookmark the actor to support visibility |

### How to use the OpenAlex MCP Server

1. **Open the actor** on Apify: [apify.com/dltik/mcp-server-openalex](https://apify.com/dltik/mcp-server-openalex).
2. **Discover the tool catalog** — run once with `mode=list_tools` (free, no charge).
3. **Pick a tool** — `search_works`, `get_work`, `search_authors`, etc.
4. **Pass arguments** — e.g. `{"query": "climate change", "limit": 10}`.
5. **Wire it into your agent** — Claude / Cursor / Continue / LangChain / LlamaIndex, via Apify's MCP bridge or `run-sync-get-dataset-items`.

### How much does the OpenAlex MCP Server cost?

**PAY_PER_EVENT — $0.005 per tool-call.** `list_tools` mode is free (discovery). Failed runs are not charged.

| Plan | Tool calls | Cost |
|---|---|---|
| Quick | 100 | $0.50 |
| Standard | 1,000 | $5.00 |
| Deep | 10,000 | $50.00 |
| Massive | 100,000 | $500.00 |

Per call, the OpenAlex MCP Server averages ~5 s of compute on 256 MB RAM and ~0.05 MB of bandwidth — fully covered by the per-event price.

### Input parameters

| Field | Type | Description |
|---|---|---|
| `mode` | enum | `call_tool` (charged) or `list_tools` (free). |
| `tool_name` | enum | `search_works`, `get_work`, `search_authors`, `get_author`, `search_institutions`, `get_institution`, `search_sources`, `search_concepts`. |
| `arguments` | object | Tool arguments — see `list_tools` for the per-tool schema. |
| `mailto` | string | Optional. Email passed to OpenAlex's polite-pool (~100k req/day). |
| `reconstruct_abstract` | bool | Default `true` — rebuilds the abstract from OpenAlex's inverted index. |

### Output example

```json
{
  "openalex_id": "W2741809807",
  "doi": "https://doi.org/10.7717/peerj.4375",
  "title": "The state of OA: a large-scale analysis of the prevalence and impact of Open Access articles",
  "type": "article",
  "publication_year": 2018,
  "publication_date": "2018-02-13",
  "language": "en",
  "cited_by_count": 1132,
  "referenced_works_count": 62,
  "open_access_status": "gold",
  "open_access_url": "https://peerj.com/articles/4375.pdf",
  "is_oa": true,
  "primary_source": {
    "id": "S2738156628",
    "display_name": "PeerJ",
    "issn_l": "2167-8359",
    "type": "journal"
  },
  "authors": ["Heather Piwowar", "Jason Priem", "Vincent Larivière", "Juan Pablo Alperin"],
  "n_authors": 8,
  "concepts": [
    {"id": "C2779343474", "display_name": "Open access", "level": 2, "score": 0.94}
  ],
  "abstract": "Despite growing interest in Open Access (OA) to scholarly literature ..."
}
````

### Use cases for the OpenAlex MCP Server

- **AI research assistants** — let Claude or Cursor pull verifiable citations from OpenAlex during a literature review.
- **Grant writing & competitive intelligence** — surface the top-cited authors and institutions on a topic in seconds.
- **CRM / lead-gen enrichment for academic SaaS** — enrich researcher records with h-index, ORCID, last institution, country.
- **Bibliometric dashboards** — track OA adoption, citation counts, journal coverage across a discipline.
- **Knowledge graph builders** — pull OpenAlex's concept taxonomy and author–institution edges into Neo4j / RDF.
- **Systematic reviews** — automate PRISMA-style filtering by year, OA status, concept score.

### API examples

**Python (Apify client):**

```python
from apify_client import ApifyClient

client = ApifyClient("YOUR_APIFY_TOKEN")
run = client.actor("dltik/mcp-server-openalex").call(run_input={
    "mode": "call_tool",
    "tool_name": "search_works",
    "arguments": {"query": "transformer architecture", "limit": 5, "sort": "cited_by_count:desc"}
})
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item.get("title"), "—", item.get("cited_by_count"))
```

**curl (synchronous run):**

```bash
curl -X POST "https://api.apify.com/v2/acts/dltik~mcp-server-openalex/run-sync-get-dataset-items?token=YOUR_APIFY_TOKEN" \
  -H "Content-Type: application/json" \
  -d '{
    "mode": "call_tool",
    "tool_name": "get_work",
    "arguments": {"doi": "10.1038/s41586-021-03819-2"}
  }'
```

### FAQ — OpenAlex MCP Server

**Why use the OpenAlex MCP Server on Apify vs hitting api.openalex.org directly?**
Maintenance + agent-shape. The OpenAlex MCP Server normalizes nested objects, reconstructs abstracts from OpenAlex's inverted index, exposes a tool dispatcher native to MCP, and bills per call — no rate-limit handling, no schema-drift code in your agent.

**What's the polite pool and do I need to set `mailto`?**
OpenAlex serves anonymous requests with stricter limits. Passing an email via `mailto` puts you in the "polite pool" with ~100k req/day. The OpenAlex MCP Server injects a maintainer email by default — set your own `mailto` for high-volume usage.

**How are abstracts returned?**
OpenAlex stores abstracts as an inverted index (`{word: [positions]}`). When `reconstruct_abstract: true` (default), the OpenAlex MCP Server rebuilds the plain-text abstract into the `abstract` field. The raw `abstract_inverted_index` is always kept too.

**How do I paginate beyond 10,000 results?**
Pass `cursor: "*"` on the first call, then forward the next cursor returned by OpenAlex. The OpenAlex MCP Server exposes the `cursor` argument on `search_works` for exactly this.

**Is there a free tier?**
`list_tools` is always free (catalog discovery). `call_tool` is $0.005 per call, billed only on success.

***

⭐ **Found the OpenAlex MCP Server useful? Bookmark it** — it's the strongest signal for Apify Store visibility.
[→ Bookmark on the Apify Store](https://apify.com/dltik/mcp-server-openalex)

#### Other scrapers by dltik

| Actor | What it does |
|---|---|
| [arXiv MCP Server](https://apify.com/dltik/mcp-server-arxiv) | 5 tools for AI agents to query arXiv preprints |
| [HackerNews MCP Server](https://apify.com/dltik/mcp-server-hackernews) | 6 tools over 30M HN stories + comments |
| [SEC EDGAR MCP Server](https://apify.com/dltik/mcp-server-sec-edgar) | 10K+ US public companies for AI agents |
| [Pappers MCP Server](https://apify.com/dltik/mcp-server-pappers) | 26M French companies (SIREN, dirigeants, finances) |
| [Wikipedia MCP Server](https://apify.com/dltik/mcp-server-wikipedia) | Search, summaries, sections — all Wikipedia languages |

License: MIT · Author: [dltik](https://apify.com/dltik)

# Actor input Schema

## `mode` (type: `string`):

list\_tools (free, returns tool catalog) or call\_tool (charged $0.005 per call).

## `tool_name` (type: `string`):

Tool to invoke. Use mode=list\_tools to discover the full catalog and per-tool arguments.

## `arguments` (type: `object`):

Arguments for the chosen tool. See list\_tools mode for full schema per tool. Example: {"query": "climate change", "limit": 10}.

## `mailto` (type: `string`):

Email passed to OpenAlex via the mailto query param to unlock the 'polite pool' (~100k req/day). If empty, falls back to the maintainer's email env var. Recommended for heavy use.

## `reconstruct_abstract` (type: `boolean`):

OpenAlex returns abstracts as an inverted index (word -> \[positions]). When true, the actor reconstructs the plain-text abstract and adds it as 'abstract' on each work record.

## Actor input object example

```json
{
  "mode": "call_tool",
  "tool_name": "search_works",
  "arguments": {
    "query": "climate change",
    "limit": 5
  },
  "reconstruct_abstract": true
}
```

# API

You can run this Actor programmatically using our API. Below are code examples in JavaScript, Python, and CLI, as well as the OpenAPI specification and MCP server setup.

## JavaScript example

```javascript
import { ApifyClient } from 'apify-client';

// Initialize the ApifyClient with your Apify API token
// Replace the '<YOUR_API_TOKEN>' with your token
const client = new ApifyClient({
    token: '<YOUR_API_TOKEN>',
});

// Prepare Actor input
const input = {
    "arguments": {
        "query": "climate change",
        "limit": 5
    }
};

// Run the Actor and wait for it to finish
const run = await client.actor("dltik/mcp-server-openalex").call(input);

// Fetch and print Actor results from the run's dataset (if any)
console.log('Results from dataset');
console.log(`💾 Check your data here: https://console.apify.com/storage/datasets/${run.defaultDatasetId}`);
const { items } = await client.dataset(run.defaultDatasetId).listItems();
items.forEach((item) => {
    console.dir(item);
});

// 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/js/docs

```

## Python example

```python
from apify_client import ApifyClient

# Initialize the ApifyClient with your Apify API token
# Replace '<YOUR_API_TOKEN>' with your token.
client = ApifyClient("<YOUR_API_TOKEN>")

# Prepare the Actor input
run_input = { "arguments": {
        "query": "climate change",
        "limit": 5,
    } }

# Run the Actor and wait for it to finish
run = client.actor("dltik/mcp-server-openalex").call(run_input=run_input)

# Fetch and print Actor results from the run's dataset (if there are any)
print("💾 Check your data here: https://console.apify.com/storage/datasets/" + run["defaultDatasetId"])
for item in client.dataset(run["defaultDatasetId"]).iterate_items():
    print(item)

# 📚 Want to learn more 📖? Go to → https://docs.apify.com/api/client/python/docs/quick-start

```

## CLI example

```bash
echo '{
  "arguments": {
    "query": "climate change",
    "limit": 5
  }
}' |
apify call dltik/mcp-server-openalex --silent --output-dataset

```

## MCP server setup

```json
{
    "mcpServers": {
        "apify": {
            "command": "npx",
            "args": [
                "mcp-remote",
                "https://mcp.apify.com/?tools=dltik/mcp-server-openalex",
                "--header",
                "Authorization: Bearer <YOUR_API_TOKEN>"
            ]
        }
    }
}

```

## OpenAPI specification

```json
{
    "openapi": "3.0.1",
    "info": {
        "title": "OpenAlex MCP Server: 200M Papers & Authors for AI Agents",
        "description": "MCP server exposing 8 tools to query OpenAlex (200M+ scholarly works, authors, institutions, concepts, sources). HTTP-only, no API key. $0.005/tool-call.",
        "version": "0.0",
        "x-build-id": "IfzOR3Yt3WynoY5ch"
    },
    "servers": [
        {
            "url": "https://api.apify.com/v2"
        }
    ],
    "paths": {
        "/acts/dltik~mcp-server-openalex/run-sync-get-dataset-items": {
            "post": {
                "operationId": "run-sync-get-dataset-items-dltik-mcp-server-openalex",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for its completion, and returns Actor's dataset items in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        },
        "/acts/dltik~mcp-server-openalex/runs": {
            "post": {
                "operationId": "runs-sync-dltik-mcp-server-openalex",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor and returns information about the initiated run in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK",
                        "content": {
                            "application/json": {
                                "schema": {
                                    "$ref": "#/components/schemas/runsResponseSchema"
                                }
                            }
                        }
                    }
                }
            }
        },
        "/acts/dltik~mcp-server-openalex/run-sync": {
            "post": {
                "operationId": "run-sync-dltik-mcp-server-openalex",
                "x-openai-isConsequential": false,
                "summary": "Executes an Actor, waits for completion, and returns the OUTPUT from Key-value store in response.",
                "tags": [
                    "Run Actor"
                ],
                "requestBody": {
                    "required": true,
                    "content": {
                        "application/json": {
                            "schema": {
                                "$ref": "#/components/schemas/inputSchema"
                            }
                        }
                    }
                },
                "parameters": [
                    {
                        "name": "token",
                        "in": "query",
                        "required": true,
                        "schema": {
                            "type": "string"
                        },
                        "description": "Enter your Apify token here"
                    }
                ],
                "responses": {
                    "200": {
                        "description": "OK"
                    }
                }
            }
        }
    },
    "components": {
        "schemas": {
            "inputSchema": {
                "type": "object",
                "required": [
                    "mode"
                ],
                "properties": {
                    "mode": {
                        "title": "Mode",
                        "enum": [
                            "call_tool",
                            "list_tools"
                        ],
                        "type": "string",
                        "description": "list_tools (free, returns tool catalog) or call_tool (charged $0.005 per call).",
                        "default": "call_tool"
                    },
                    "tool_name": {
                        "title": "Tool name",
                        "enum": [
                            "search_works",
                            "get_work",
                            "search_authors",
                            "get_author",
                            "search_institutions",
                            "get_institution",
                            "search_sources",
                            "search_concepts"
                        ],
                        "type": "string",
                        "description": "Tool to invoke. Use mode=list_tools to discover the full catalog and per-tool arguments.",
                        "default": "search_works"
                    },
                    "arguments": {
                        "title": "Arguments",
                        "type": "object",
                        "description": "Arguments for the chosen tool. See list_tools mode for full schema per tool. Example: {\"query\": \"climate change\", \"limit\": 10}."
                    },
                    "mailto": {
                        "title": "Polite-pool email (optional)",
                        "type": "string",
                        "description": "Email passed to OpenAlex via the mailto query param to unlock the 'polite pool' (~100k req/day). If empty, falls back to the maintainer's email env var. Recommended for heavy use."
                    },
                    "reconstruct_abstract": {
                        "title": "Reconstruct abstracts to plain text",
                        "type": "boolean",
                        "description": "OpenAlex returns abstracts as an inverted index (word -> [positions]). When true, the actor reconstructs the plain-text abstract and adds it as 'abstract' on each work record.",
                        "default": true
                    }
                }
            },
            "runsResponseSchema": {
                "type": "object",
                "properties": {
                    "data": {
                        "type": "object",
                        "properties": {
                            "id": {
                                "type": "string"
                            },
                            "actId": {
                                "type": "string"
                            },
                            "userId": {
                                "type": "string"
                            },
                            "startedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "finishedAt": {
                                "type": "string",
                                "format": "date-time",
                                "example": "2025-01-08T00:00:00.000Z"
                            },
                            "status": {
                                "type": "string",
                                "example": "READY"
                            },
                            "meta": {
                                "type": "object",
                                "properties": {
                                    "origin": {
                                        "type": "string",
                                        "example": "API"
                                    },
                                    "userAgent": {
                                        "type": "string"
                                    }
                                }
                            },
                            "stats": {
                                "type": "object",
                                "properties": {
                                    "inputBodyLen": {
                                        "type": "integer",
                                        "example": 2000
                                    },
                                    "rebootCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "restartCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "resurrectCount": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "computeUnits": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "options": {
                                "type": "object",
                                "properties": {
                                    "build": {
                                        "type": "string",
                                        "example": "latest"
                                    },
                                    "timeoutSecs": {
                                        "type": "integer",
                                        "example": 300
                                    },
                                    "memoryMbytes": {
                                        "type": "integer",
                                        "example": 1024
                                    },
                                    "diskMbytes": {
                                        "type": "integer",
                                        "example": 2048
                                    }
                                }
                            },
                            "buildId": {
                                "type": "string"
                            },
                            "defaultKeyValueStoreId": {
                                "type": "string"
                            },
                            "defaultDatasetId": {
                                "type": "string"
                            },
                            "defaultRequestQueueId": {
                                "type": "string"
                            },
                            "buildNumber": {
                                "type": "string",
                                "example": "1.0.0"
                            },
                            "containerUrl": {
                                "type": "string"
                            },
                            "usage": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "integer",
                                        "example": 1
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            },
                            "usageTotalUsd": {
                                "type": "number",
                                "example": 0.00005
                            },
                            "usageUsd": {
                                "type": "object",
                                "properties": {
                                    "ACTOR_COMPUTE_UNITS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATASET_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "KEY_VALUE_STORE_WRITES": {
                                        "type": "number",
                                        "example": 0.00005
                                    },
                                    "KEY_VALUE_STORE_LISTS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_READS": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "REQUEST_QUEUE_WRITES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_INTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "DATA_TRANSFER_EXTERNAL_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_RESIDENTIAL_TRANSFER_GBYTES": {
                                        "type": "integer",
                                        "example": 0
                                    },
                                    "PROXY_SERPS": {
                                        "type": "integer",
                                        "example": 0
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
```
