entryscape 1.3.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- entryscape/__init__.py +219 -0
- entryscape/api/__init__.py +26 -0
- entryscape/api/app_token_api.py +835 -0
- entryscape/api/auth_api.py +965 -0
- entryscape/api/catalog_api.py +4473 -0
- entryscape/api/contact_api.py +3135 -0
- entryscape/api/dataservice_api.py +3135 -0
- entryscape/api/dataset_api.py +3804 -0
- entryscape/api/distribution_api.py +3135 -0
- entryscape/api/document_api.py +2740 -0
- entryscape/api/idea_api.py +2740 -0
- entryscape/api/job_api.py +360 -0
- entryscape/api/model_api.py +2446 -0
- entryscape/api/model_class_api.py +2611 -0
- entryscape/api/model_diagram_api.py +2611 -0
- entryscape/api/model_field_api.py +2611 -0
- entryscape/api/model_form_api.py +2611 -0
- entryscape/api/model_namespace_api.py +2611 -0
- entryscape/api/model_property_api.py +2611 -0
- entryscape/api/organization_api.py +3135 -0
- entryscape/api/search_api.py +1513 -0
- entryscape/api/showcase_api.py +2740 -0
- entryscape/api/suggestion_api.py +2740 -0
- entryscape/api/terminology_api.py +1726 -0
- entryscape/api/upload_api.py +814 -0
- entryscape/api_client.py +763 -0
- entryscape/api_response.py +20 -0
- entryscape/configuration.py +673 -0
- entryscape/exceptions.py +222 -0
- entryscape/models/__init__.py +71 -0
- entryscape/models/app_token.py +156 -0
- entryscape/models/app_token_create_request.py +98 -0
- entryscape/models/app_token_create_response.py +169 -0
- entryscape/models/app_token_status.py +35 -0
- entryscape/models/app_token_verify_request.py +109 -0
- entryscape/models/entity_list_item.py +175 -0
- entryscape/models/entity_list_response.py +139 -0
- entryscape/models/entity_reference.py +189 -0
- entryscape/models/entity_type.py +57 -0
- entryscape/models/error.py +135 -0
- entryscape/models/facet_field.py +115 -0
- entryscape/models/facet_field_values_inner.py +100 -0
- entryscape/models/facet_list.py +113 -0
- entryscape/models/facet_list_available_inner.py +107 -0
- entryscape/models/job_response.py +112 -0
- entryscape/models/job_status.py +189 -0
- entryscape/models/job_status_progress.py +100 -0
- entryscape/models/job_status_value.py +37 -0
- entryscape/models/job_type.py +37 -0
- entryscape/models/list_catalogs_entry_type_parameter.py +37 -0
- entryscape/models/list_catalogs_graph_type_parameter.py +44 -0
- entryscape/models/list_catalogs_resource_type_parameter.py +37 -0
- entryscape/models/login_request.py +108 -0
- entryscape/models/login_response.py +115 -0
- entryscape/models/metadata_format.py +38 -0
- entryscape/models/metadata_request.py +99 -0
- entryscape/models/parent_reference.py +130 -0
- entryscape/models/search_request.py +158 -0
- entryscape/models/search_request_filters.py +144 -0
- entryscape/models/search_request_filters_graph_type.py +44 -0
- entryscape/models/search_request_filters_resource_type.py +37 -0
- entryscape/models/search_request_sort.py +38 -0
- entryscape/models/search_request_sort_order.py +35 -0
- entryscape/models/search_response.py +175 -0
- entryscape/models/search_result.py +175 -0
- entryscape/models/search_result_graph_type.py +44 -0
- entryscape/models/search_result_resource_type.py +37 -0
- entryscape/models/search_sort_order_parameter.py +35 -0
- entryscape/models/terminology_import_mode.py +35 -0
- entryscape/models/validation_profile.py +36 -0
- entryscape/models/validation_response.py +149 -0
- entryscape/models/validation_result.py +162 -0
- entryscape/models/validation_severity.py +36 -0
- entryscape/models/validation_summary.py +122 -0
- entryscape/models/whoami_response.py +104 -0
- entryscape/py.typed +0 -0
- entryscape/rest.py +210 -0
- entryscape-1.3.0.dist-info/METADATA +696 -0
- entryscape-1.3.0.dist-info/RECORD +82 -0
- entryscape-1.3.0.dist-info/WHEEL +5 -0
- entryscape-1.3.0.dist-info/licenses/LICENSE.txt +165 -0
- entryscape-1.3.0.dist-info/top_level.txt +1 -0
entryscape/exceptions.py
ADDED
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
"""
|
|
2
|
+
EntryScape API
|
|
3
|
+
|
|
4
|
+
This API provides structured access to EntryScape data following DCAT-AP standards. All entity endpoints follow a consistent pattern: - GET /{entity} - List all entities - POST /{entity} - Create a new entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id} - Get entity reference info - DELETE /{entity}/{context_id}/{entry_id} - Delete an entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id}/metadata - Get raw RDF metadata - PUT /{entity}/{context_id}/{entry_id}/metadata - Replace raw RDF metadata (JSON-LD, Turtle, RDF/XML, or N-Triples passthrough) ## Reading and consistency Reads of a single entity — `GET /{entity}/{context_id}/{entry_id}` and its `/metadata` — go straight to the store and always reflect the latest write. Lists and search do not. They are answered from a search index that is updated asynchronously, so for a short interval after a write: - an entry that was just created may be missing from the list it belongs to, while reading it by id already works; - an entry that was just deleted may still appear; - `results` counts what the index holds, not what the store holds. Write a client that tolerates this: after creating an entry, use the id the create returned rather than searching for what you just wrote. `results` is also an upper bound rather than an exact count when the caller cannot read every hit (see the field's own description). Separately, responses are cached for 15 minutes by default, which is a second and unrelated source of staleness. A write invalidates the cache entries it affects, but a read that lands in the indexing gap can cache a list that does not yet show it. ## Authentication Most endpoints require an authenticated session. Reading public data works without authentication, while creating, updating, and deleting entities requires a valid user session. 1. **Log in** with `POST /auth/login`, supplying an EntryStore username and password. The response returns an `auth_token`. 2. **Send the token** on subsequent requests via the `X-Auth-Token` header. This is the recommended method for SDK and programmatic access. Browser clients can instead rely on the `auth_token` cookie, which the server sets automatically on login (`SameSite=Lax; Secure`). 3. **Inspect the session** with `GET /auth/whoami`, which returns the current user or an anonymous/guest identity when no valid token is supplied. 4. **Log out** with `POST /auth/logout` to invalidate the token and clear the cookie. ### Writes and CSRF Send the token as the `X-Auth-Token` **header** and there is nothing else to do: creates, updates and deletes work as they are, which is how the SDKs and the MCP server are built. The `auth_token` cookie is different. A cookie is attached by the browser automatically, so a state-changing request authenticated by it alone is what cross-site request forgery abuses, and those requests additionally need a double-submit token: read the `XSRF-TOKEN` cookie the server sets on any response and send its value back as the `X-XSRF-TOKEN` header. Without it the request is rejected with `403` before it reaches the endpoint. A custom header cannot be forged this way — cross-origin JavaScript cannot set one without a CORS preflight, which this API grants only to configured origins — so the header is the recommended path for anything that is not a browser session. ### App Tokens An app token identifies an integration. Create one with `POST /app-token` (unauthenticated). Creation requires an email address: the token starts in the `pending` state and a 6-digit code is emailed to the owner. Confirm the code with `POST /app-token/verify` to activate the token and receive its value, which is shown only once — store it securely. `GET /app-token/{app_token_id}` then returns its details and quota usage, authenticated by the token itself via the `X-App-Token` header. **The `X-App-Token` header is accepted only by `GET /app-token/{app_token_id}`.** No other operation reads it, and the read quota is recorded but not yet enforced: reading public data needs no token of any kind, and sending an app token with a read neither grants nor meters anything. Metered public read access is the intent of the two-token model, not a description of this release — see the `security` declared on each operation for what it actually accepts. ## SDKs Generated clients for Python, TypeScript, JavaScript and C# wrap every operation below, and each operation on this page shows the SDK call beside the `curl` command. Two of them install from a public registry: ``` pip install entryscape # Python npm install @entryscape/api-client # TypeScript and JavaScript ``` `@entryscape/api-client` ships an ESM and a CommonJS build, so `import` and `require()` both work and TypeScript is not required to use it — the name describes what it is written in, not what you have to write. There is a separate generated JavaScript SDK, and it is deliberately **not** on npm; this package is the npm client for both languages. Every SDK, including the C# and JavaScript ones, is also published as a tarball. The downloads are linked in the sidebar, they are the route to take when a registry is unreachable or a build has to vendor its dependencies, and each archive carries the SDK's own README: - **Python** — `pip install entryscape`, or `python-sdk.tar.gz`. - **TypeScript and JavaScript** — `npm install @entryscape/api-client`, or `typescript-sdk.tar.gz`. - **JavaScript (the separate generated client)** — not published to a registry, by design; `javascript-sdk.tar.gz` only. - **C#** — not published to NuGet yet; `csharp-sdk.tar.gz` only. - **MCP server** — `npx -y @entryscape/mcp-server`, or `mcp-server.tar.gz`. ## MCP Server A Model Context Protocol (MCP) server is generated from this specification and published alongside the SDKs (`@entryscape/mcp-server`), exposing the API as tools for AI assistants and agents. Each operation becomes one tool named after its `operationId` — `listDatasets`, `createCatalog`, `search`, `getJobStatus`. The `auth` operations are the exception: the server reads its token once at startup, so a `login` tool could install nothing and a `logout` tool would only invalidate the token the server was started with. On connect the server also hands the assistant a short set of rules that hold across every tool: a token is bound to one EntryStore instance, the search index lags a write, uploads finish as jobs. ### Install The server is on npm, and MCP servers are normally launched straight from there, so nothing has to be installed first: ``` npx -y @entryscape/mcp-server ``` `npm install -g @entryscape/mcp-server` works too and puts an `entryscape-mcp` binary on `PATH`. It is also listed in the MCP Registry as `com.entryscape/mcp-server`, which is where clients and marketplaces look it up. Without npm, download `mcp-server.tar.gz` from the SDK downloads and unpack it. The package ships prebuilt; install its runtime dependency once: ``` mkdir entryscape-mcp && tar -xzf mcp-server.tar.gz -C entryscape-mcp cd entryscape-mcp && npm install --omit=dev ``` ### Configure The server is a stdio MCP server configured through environment variables, read once at startup: - `ENTRYSCAPE_API_URL` — base URL of the API server, path included. Required: there is no default, and the server refuses to start without it. - `ENTRYSCAPE_AUTH_TOKEN` — a session token from `POST /auth/login`, sent as the `X-Auth-Token` header. Optional: without it reads see public entries and every write is refused. A token is valid only for the EntryStore instance that issued it. - `ENTRYSCAPE_ENTRYSTORE_HOST` — the EntryStore instance the API server should use, for example `dev.entryscape.com/store/`. Unset means the API server's own default. - `ENTRYSCAPE_MCP_UPLOAD_ROOT` — the directory the upload tools may read files from. **Unset means uploads are disabled**: `addFileToDistribution`, `replaceFileInDistribution` and `importTerminology` refuse and say so. - `ENTRYSCAPE_MCP_TAGS` — comma-separated tags to serve tools for, for example `catalog,dataset,distribution,search`. Unset serves every tool. The package's `README.md` lists the remaining variables (timeouts and size caps). Register the server with any MCP-capable client by adding it to the client's `mcpServers` configuration: ```json { \"mcpServers\": { \"entryscape\": { \"command\": \"npx\", \"args\": [\"-y\", \"@entryscape/mcp-server\"], \"env\": { \"ENTRYSCAPE_API_URL\": \"https://<your EntryScape API host>\", \"ENTRYSCAPE_AUTH_TOKEN\": \"your-session-token\" } } } } ``` From an unpacked download instead, the command is `\"command\": \"node\", \"args\": [\"/path/to/entryscape-mcp/dist/bin/entryscape-mcp.js\"]`. ### Claude Code The unpacked package is also a Claude Code plugin, so one install gives the server and the skills below together. Claude Code installs plugins from marketplaces, and the package carries a one-entry marketplace pointing at itself: ``` claude plugin marketplace add /path/to/entryscape-mcp claude plugin install entryscape@entryscape --scope user ``` The plugin passes `ENTRYSCAPE_API_URL`, `ENTRYSCAPE_AUTH_TOKEN`, `ENTRYSCAPE_ENTRYSTORE_HOST`, `ENTRYSCAPE_MCP_TAGS` and `ENTRYSCAPE_MCP_UPLOAD_ROOT` through from the shell Claude Code was started in, so set them there. To try the plugin for one session without installing it, start Claude Code with `--plugin-dir /path/to/entryscape-mcp`. ### Skills `skills/` in the package holds procedures that span several tools, in the Agent Skills format (a `SKILL.md` with `name` and `description`): which tools to call in which order, what to check between calls, and where to stop. Claude Code loads them from the plugin; other clients that read the format can be pointed at the directory. The `find` skill covers discovery, facets and selective metadata reads; `publish-dataset` takes a dataset from catalog to validated distribution with its file; `edit-metadata` changes an entry that already exists without dropping the rest of its graph, models included; `migrate-from-taskrunner` moves an integration off the deprecated Taskrunner API. Every tool a skill names is checked against this specification when the package is built. ## Deprecation Policy When endpoints or features are deprecated: 1. The operation is marked with `deprecated: true` in this spec 2. The operation description documents the replacement endpoint and sunset date 3. Deprecated endpoints remain functional for at least 6 months after announcement 4. The server returns a `Sunset` header with the planned removal date 5. After the sunset date, the endpoint may be removed in a future release
|
|
5
|
+
|
|
6
|
+
The version of the OpenAPI document: 1.3.0
|
|
7
|
+
Contact: contact@entryscape.com
|
|
8
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
9
|
+
|
|
10
|
+
Do not edit the class manually.
|
|
11
|
+
""" # noqa: E501
|
|
12
|
+
|
|
13
|
+
from typing import Any, Optional
|
|
14
|
+
from typing_extensions import Self
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class OpenApiException(Exception):
|
|
18
|
+
"""The base exception class for all OpenAPIExceptions"""
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class ApiTypeError(OpenApiException, TypeError):
|
|
22
|
+
def __init__(
|
|
23
|
+
self, msg, path_to_item=None, valid_classes=None, key_type=None
|
|
24
|
+
) -> None:
|
|
25
|
+
"""Raises an exception for TypeErrors
|
|
26
|
+
|
|
27
|
+
Args:
|
|
28
|
+
msg (str): the exception message
|
|
29
|
+
|
|
30
|
+
Keyword Args:
|
|
31
|
+
path_to_item (list): a list of keys an indices to get to the
|
|
32
|
+
current_item
|
|
33
|
+
None if unset
|
|
34
|
+
valid_classes (tuple): the primitive classes that current item
|
|
35
|
+
should be an instance of
|
|
36
|
+
None if unset
|
|
37
|
+
key_type (bool): False if our value is a value in a dict
|
|
38
|
+
True if it is a key in a dict
|
|
39
|
+
False if our item is an item in a list
|
|
40
|
+
None if unset
|
|
41
|
+
"""
|
|
42
|
+
self.path_to_item = path_to_item
|
|
43
|
+
self.valid_classes = valid_classes
|
|
44
|
+
self.key_type = key_type
|
|
45
|
+
full_msg = msg
|
|
46
|
+
if path_to_item:
|
|
47
|
+
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
|
|
48
|
+
super(ApiTypeError, self).__init__(full_msg)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class ApiValueError(OpenApiException, ValueError):
|
|
52
|
+
def __init__(self, msg, path_to_item=None) -> None:
|
|
53
|
+
"""
|
|
54
|
+
Args:
|
|
55
|
+
msg (str): the exception message
|
|
56
|
+
|
|
57
|
+
Keyword Args:
|
|
58
|
+
path_to_item (list) the path to the exception in the
|
|
59
|
+
received_data dict. None if unset
|
|
60
|
+
"""
|
|
61
|
+
|
|
62
|
+
self.path_to_item = path_to_item
|
|
63
|
+
full_msg = msg
|
|
64
|
+
if path_to_item:
|
|
65
|
+
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
|
|
66
|
+
super(ApiValueError, self).__init__(full_msg)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class ApiAttributeError(OpenApiException, AttributeError):
|
|
70
|
+
def __init__(self, msg, path_to_item=None) -> None:
|
|
71
|
+
"""
|
|
72
|
+
Raised when an attribute reference or assignment fails.
|
|
73
|
+
|
|
74
|
+
Args:
|
|
75
|
+
msg (str): the exception message
|
|
76
|
+
|
|
77
|
+
Keyword Args:
|
|
78
|
+
path_to_item (None/list) the path to the exception in the
|
|
79
|
+
received_data dict
|
|
80
|
+
"""
|
|
81
|
+
self.path_to_item = path_to_item
|
|
82
|
+
full_msg = msg
|
|
83
|
+
if path_to_item:
|
|
84
|
+
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
|
|
85
|
+
super(ApiAttributeError, self).__init__(full_msg)
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
class ApiKeyError(OpenApiException, KeyError):
|
|
89
|
+
def __init__(self, msg, path_to_item=None) -> None:
|
|
90
|
+
"""
|
|
91
|
+
Args:
|
|
92
|
+
msg (str): the exception message
|
|
93
|
+
|
|
94
|
+
Keyword Args:
|
|
95
|
+
path_to_item (None/list) the path to the exception in the
|
|
96
|
+
received_data dict
|
|
97
|
+
"""
|
|
98
|
+
self.path_to_item = path_to_item
|
|
99
|
+
full_msg = msg
|
|
100
|
+
if path_to_item:
|
|
101
|
+
full_msg = "{0} at {1}".format(msg, render_path(path_to_item))
|
|
102
|
+
super(ApiKeyError, self).__init__(full_msg)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class ApiException(OpenApiException):
|
|
106
|
+
|
|
107
|
+
def __init__(
|
|
108
|
+
self,
|
|
109
|
+
status=None,
|
|
110
|
+
reason=None,
|
|
111
|
+
http_resp=None,
|
|
112
|
+
*,
|
|
113
|
+
body: Optional[str] = None,
|
|
114
|
+
data: Optional[Any] = None,
|
|
115
|
+
) -> None:
|
|
116
|
+
self.status = status
|
|
117
|
+
self.reason = reason
|
|
118
|
+
self.body = body
|
|
119
|
+
self.data = data
|
|
120
|
+
self.headers = None
|
|
121
|
+
|
|
122
|
+
if http_resp:
|
|
123
|
+
if self.status is None:
|
|
124
|
+
self.status = http_resp.status
|
|
125
|
+
if self.reason is None:
|
|
126
|
+
self.reason = http_resp.reason
|
|
127
|
+
if self.body is None:
|
|
128
|
+
try:
|
|
129
|
+
self.body = http_resp.data.decode("utf-8")
|
|
130
|
+
except Exception:
|
|
131
|
+
pass
|
|
132
|
+
self.headers = http_resp.headers
|
|
133
|
+
|
|
134
|
+
@classmethod
|
|
135
|
+
def from_response(
|
|
136
|
+
cls,
|
|
137
|
+
*,
|
|
138
|
+
http_resp,
|
|
139
|
+
body: Optional[str],
|
|
140
|
+
data: Optional[Any],
|
|
141
|
+
) -> Self:
|
|
142
|
+
if http_resp.status == 400:
|
|
143
|
+
raise BadRequestException(http_resp=http_resp, body=body, data=data)
|
|
144
|
+
|
|
145
|
+
if http_resp.status == 401:
|
|
146
|
+
raise UnauthorizedException(http_resp=http_resp, body=body, data=data)
|
|
147
|
+
|
|
148
|
+
if http_resp.status == 403:
|
|
149
|
+
raise ForbiddenException(http_resp=http_resp, body=body, data=data)
|
|
150
|
+
|
|
151
|
+
if http_resp.status == 404:
|
|
152
|
+
raise NotFoundException(http_resp=http_resp, body=body, data=data)
|
|
153
|
+
|
|
154
|
+
# Added new conditions for 409 and 422
|
|
155
|
+
if http_resp.status == 409:
|
|
156
|
+
raise ConflictException(http_resp=http_resp, body=body, data=data)
|
|
157
|
+
|
|
158
|
+
if http_resp.status == 422:
|
|
159
|
+
raise UnprocessableEntityException(
|
|
160
|
+
http_resp=http_resp, body=body, data=data
|
|
161
|
+
)
|
|
162
|
+
|
|
163
|
+
if 500 <= http_resp.status <= 599:
|
|
164
|
+
raise ServiceException(http_resp=http_resp, body=body, data=data)
|
|
165
|
+
raise ApiException(http_resp=http_resp, body=body, data=data)
|
|
166
|
+
|
|
167
|
+
def __str__(self):
|
|
168
|
+
"""Custom error messages for exception"""
|
|
169
|
+
error_message = "({0})\n" "Reason: {1}\n".format(self.status, self.reason)
|
|
170
|
+
if self.headers:
|
|
171
|
+
error_message += "HTTP response headers: {0}\n".format(self.headers)
|
|
172
|
+
|
|
173
|
+
if self.body:
|
|
174
|
+
error_message += "HTTP response body: {0}\n".format(self.body)
|
|
175
|
+
|
|
176
|
+
if self.data:
|
|
177
|
+
error_message += "HTTP response data: {0}\n".format(self.data)
|
|
178
|
+
|
|
179
|
+
return error_message
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class BadRequestException(ApiException):
|
|
183
|
+
pass
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
class NotFoundException(ApiException):
|
|
187
|
+
pass
|
|
188
|
+
|
|
189
|
+
|
|
190
|
+
class UnauthorizedException(ApiException):
|
|
191
|
+
pass
|
|
192
|
+
|
|
193
|
+
|
|
194
|
+
class ForbiddenException(ApiException):
|
|
195
|
+
pass
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class ServiceException(ApiException):
|
|
199
|
+
pass
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
class ConflictException(ApiException):
|
|
203
|
+
"""Exception for HTTP 409 Conflict."""
|
|
204
|
+
|
|
205
|
+
pass
|
|
206
|
+
|
|
207
|
+
|
|
208
|
+
class UnprocessableEntityException(ApiException):
|
|
209
|
+
"""Exception for HTTP 422 Unprocessable Entity."""
|
|
210
|
+
|
|
211
|
+
pass
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
def render_path(path_to_item):
|
|
215
|
+
"""Returns a string representation of a path"""
|
|
216
|
+
result = ""
|
|
217
|
+
for pth in path_to_item:
|
|
218
|
+
if isinstance(pth, int):
|
|
219
|
+
result += "[{0}]".format(pth)
|
|
220
|
+
else:
|
|
221
|
+
result += "['{0}']".format(pth)
|
|
222
|
+
return result
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
# flake8: noqa
|
|
4
|
+
"""
|
|
5
|
+
EntryScape API
|
|
6
|
+
|
|
7
|
+
This API provides structured access to EntryScape data following DCAT-AP standards. All entity endpoints follow a consistent pattern: - GET /{entity} - List all entities - POST /{entity} - Create a new entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id} - Get entity reference info - DELETE /{entity}/{context_id}/{entry_id} - Delete an entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id}/metadata - Get raw RDF metadata - PUT /{entity}/{context_id}/{entry_id}/metadata - Replace raw RDF metadata (JSON-LD, Turtle, RDF/XML, or N-Triples passthrough) ## Reading and consistency Reads of a single entity — `GET /{entity}/{context_id}/{entry_id}` and its `/metadata` — go straight to the store and always reflect the latest write. Lists and search do not. They are answered from a search index that is updated asynchronously, so for a short interval after a write: - an entry that was just created may be missing from the list it belongs to, while reading it by id already works; - an entry that was just deleted may still appear; - `results` counts what the index holds, not what the store holds. Write a client that tolerates this: after creating an entry, use the id the create returned rather than searching for what you just wrote. `results` is also an upper bound rather than an exact count when the caller cannot read every hit (see the field's own description). Separately, responses are cached for 15 minutes by default, which is a second and unrelated source of staleness. A write invalidates the cache entries it affects, but a read that lands in the indexing gap can cache a list that does not yet show it. ## Authentication Most endpoints require an authenticated session. Reading public data works without authentication, while creating, updating, and deleting entities requires a valid user session. 1. **Log in** with `POST /auth/login`, supplying an EntryStore username and password. The response returns an `auth_token`. 2. **Send the token** on subsequent requests via the `X-Auth-Token` header. This is the recommended method for SDK and programmatic access. Browser clients can instead rely on the `auth_token` cookie, which the server sets automatically on login (`SameSite=Lax; Secure`). 3. **Inspect the session** with `GET /auth/whoami`, which returns the current user or an anonymous/guest identity when no valid token is supplied. 4. **Log out** with `POST /auth/logout` to invalidate the token and clear the cookie. ### Writes and CSRF Send the token as the `X-Auth-Token` **header** and there is nothing else to do: creates, updates and deletes work as they are, which is how the SDKs and the MCP server are built. The `auth_token` cookie is different. A cookie is attached by the browser automatically, so a state-changing request authenticated by it alone is what cross-site request forgery abuses, and those requests additionally need a double-submit token: read the `XSRF-TOKEN` cookie the server sets on any response and send its value back as the `X-XSRF-TOKEN` header. Without it the request is rejected with `403` before it reaches the endpoint. A custom header cannot be forged this way — cross-origin JavaScript cannot set one without a CORS preflight, which this API grants only to configured origins — so the header is the recommended path for anything that is not a browser session. ### App Tokens An app token identifies an integration. Create one with `POST /app-token` (unauthenticated). Creation requires an email address: the token starts in the `pending` state and a 6-digit code is emailed to the owner. Confirm the code with `POST /app-token/verify` to activate the token and receive its value, which is shown only once — store it securely. `GET /app-token/{app_token_id}` then returns its details and quota usage, authenticated by the token itself via the `X-App-Token` header. **The `X-App-Token` header is accepted only by `GET /app-token/{app_token_id}`.** No other operation reads it, and the read quota is recorded but not yet enforced: reading public data needs no token of any kind, and sending an app token with a read neither grants nor meters anything. Metered public read access is the intent of the two-token model, not a description of this release — see the `security` declared on each operation for what it actually accepts. ## SDKs Generated clients for Python, TypeScript, JavaScript and C# wrap every operation below, and each operation on this page shows the SDK call beside the `curl` command. Two of them install from a public registry: ``` pip install entryscape # Python npm install @entryscape/api-client # TypeScript and JavaScript ``` `@entryscape/api-client` ships an ESM and a CommonJS build, so `import` and `require()` both work and TypeScript is not required to use it — the name describes what it is written in, not what you have to write. There is a separate generated JavaScript SDK, and it is deliberately **not** on npm; this package is the npm client for both languages. Every SDK, including the C# and JavaScript ones, is also published as a tarball. The downloads are linked in the sidebar, they are the route to take when a registry is unreachable or a build has to vendor its dependencies, and each archive carries the SDK's own README: - **Python** — `pip install entryscape`, or `python-sdk.tar.gz`. - **TypeScript and JavaScript** — `npm install @entryscape/api-client`, or `typescript-sdk.tar.gz`. - **JavaScript (the separate generated client)** — not published to a registry, by design; `javascript-sdk.tar.gz` only. - **C#** — not published to NuGet yet; `csharp-sdk.tar.gz` only. - **MCP server** — `npx -y @entryscape/mcp-server`, or `mcp-server.tar.gz`. ## MCP Server A Model Context Protocol (MCP) server is generated from this specification and published alongside the SDKs (`@entryscape/mcp-server`), exposing the API as tools for AI assistants and agents. Each operation becomes one tool named after its `operationId` — `listDatasets`, `createCatalog`, `search`, `getJobStatus`. The `auth` operations are the exception: the server reads its token once at startup, so a `login` tool could install nothing and a `logout` tool would only invalidate the token the server was started with. On connect the server also hands the assistant a short set of rules that hold across every tool: a token is bound to one EntryStore instance, the search index lags a write, uploads finish as jobs. ### Install The server is on npm, and MCP servers are normally launched straight from there, so nothing has to be installed first: ``` npx -y @entryscape/mcp-server ``` `npm install -g @entryscape/mcp-server` works too and puts an `entryscape-mcp` binary on `PATH`. It is also listed in the MCP Registry as `com.entryscape/mcp-server`, which is where clients and marketplaces look it up. Without npm, download `mcp-server.tar.gz` from the SDK downloads and unpack it. The package ships prebuilt; install its runtime dependency once: ``` mkdir entryscape-mcp && tar -xzf mcp-server.tar.gz -C entryscape-mcp cd entryscape-mcp && npm install --omit=dev ``` ### Configure The server is a stdio MCP server configured through environment variables, read once at startup: - `ENTRYSCAPE_API_URL` — base URL of the API server, path included. Required: there is no default, and the server refuses to start without it. - `ENTRYSCAPE_AUTH_TOKEN` — a session token from `POST /auth/login`, sent as the `X-Auth-Token` header. Optional: without it reads see public entries and every write is refused. A token is valid only for the EntryStore instance that issued it. - `ENTRYSCAPE_ENTRYSTORE_HOST` — the EntryStore instance the API server should use, for example `dev.entryscape.com/store/`. Unset means the API server's own default. - `ENTRYSCAPE_MCP_UPLOAD_ROOT` — the directory the upload tools may read files from. **Unset means uploads are disabled**: `addFileToDistribution`, `replaceFileInDistribution` and `importTerminology` refuse and say so. - `ENTRYSCAPE_MCP_TAGS` — comma-separated tags to serve tools for, for example `catalog,dataset,distribution,search`. Unset serves every tool. The package's `README.md` lists the remaining variables (timeouts and size caps). Register the server with any MCP-capable client by adding it to the client's `mcpServers` configuration: ```json { \"mcpServers\": { \"entryscape\": { \"command\": \"npx\", \"args\": [\"-y\", \"@entryscape/mcp-server\"], \"env\": { \"ENTRYSCAPE_API_URL\": \"https://<your EntryScape API host>\", \"ENTRYSCAPE_AUTH_TOKEN\": \"your-session-token\" } } } } ``` From an unpacked download instead, the command is `\"command\": \"node\", \"args\": [\"/path/to/entryscape-mcp/dist/bin/entryscape-mcp.js\"]`. ### Claude Code The unpacked package is also a Claude Code plugin, so one install gives the server and the skills below together. Claude Code installs plugins from marketplaces, and the package carries a one-entry marketplace pointing at itself: ``` claude plugin marketplace add /path/to/entryscape-mcp claude plugin install entryscape@entryscape --scope user ``` The plugin passes `ENTRYSCAPE_API_URL`, `ENTRYSCAPE_AUTH_TOKEN`, `ENTRYSCAPE_ENTRYSTORE_HOST`, `ENTRYSCAPE_MCP_TAGS` and `ENTRYSCAPE_MCP_UPLOAD_ROOT` through from the shell Claude Code was started in, so set them there. To try the plugin for one session without installing it, start Claude Code with `--plugin-dir /path/to/entryscape-mcp`. ### Skills `skills/` in the package holds procedures that span several tools, in the Agent Skills format (a `SKILL.md` with `name` and `description`): which tools to call in which order, what to check between calls, and where to stop. Claude Code loads them from the plugin; other clients that read the format can be pointed at the directory. The `find` skill covers discovery, facets and selective metadata reads; `publish-dataset` takes a dataset from catalog to validated distribution with its file; `edit-metadata` changes an entry that already exists without dropping the rest of its graph, models included; `migrate-from-taskrunner` moves an integration off the deprecated Taskrunner API. Every tool a skill names is checked against this specification when the package is built. ## Deprecation Policy When endpoints or features are deprecated: 1. The operation is marked with `deprecated: true` in this spec 2. The operation description documents the replacement endpoint and sunset date 3. Deprecated endpoints remain functional for at least 6 months after announcement 4. The server returns a `Sunset` header with the planned removal date 5. After the sunset date, the endpoint may be removed in a future release
|
|
8
|
+
|
|
9
|
+
The version of the OpenAPI document: 1.3.0
|
|
10
|
+
Contact: contact@entryscape.com
|
|
11
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
12
|
+
|
|
13
|
+
Do not edit the class manually.
|
|
14
|
+
""" # noqa: E501
|
|
15
|
+
|
|
16
|
+
# import models into model package
|
|
17
|
+
from entryscape.models.app_token import AppToken
|
|
18
|
+
from entryscape.models.app_token_create_request import AppTokenCreateRequest
|
|
19
|
+
from entryscape.models.app_token_create_response import AppTokenCreateResponse
|
|
20
|
+
from entryscape.models.app_token_status import AppTokenStatus
|
|
21
|
+
from entryscape.models.app_token_verify_request import AppTokenVerifyRequest
|
|
22
|
+
from entryscape.models.entity_list_item import EntityListItem
|
|
23
|
+
from entryscape.models.entity_list_response import EntityListResponse
|
|
24
|
+
from entryscape.models.entity_reference import EntityReference
|
|
25
|
+
from entryscape.models.entity_type import EntityType
|
|
26
|
+
from entryscape.models.error import Error
|
|
27
|
+
from entryscape.models.facet_field import FacetField
|
|
28
|
+
from entryscape.models.facet_field_values_inner import FacetFieldValuesInner
|
|
29
|
+
from entryscape.models.facet_list import FacetList
|
|
30
|
+
from entryscape.models.facet_list_available_inner import FacetListAvailableInner
|
|
31
|
+
from entryscape.models.job_response import JobResponse
|
|
32
|
+
from entryscape.models.job_status import JobStatus
|
|
33
|
+
from entryscape.models.job_status_progress import JobStatusProgress
|
|
34
|
+
from entryscape.models.job_status_value import JobStatusValue
|
|
35
|
+
from entryscape.models.job_type import JobType
|
|
36
|
+
from entryscape.models.list_catalogs_entry_type_parameter import (
|
|
37
|
+
ListCatalogsEntryTypeParameter,
|
|
38
|
+
)
|
|
39
|
+
from entryscape.models.list_catalogs_graph_type_parameter import (
|
|
40
|
+
ListCatalogsGraphTypeParameter,
|
|
41
|
+
)
|
|
42
|
+
from entryscape.models.list_catalogs_resource_type_parameter import (
|
|
43
|
+
ListCatalogsResourceTypeParameter,
|
|
44
|
+
)
|
|
45
|
+
from entryscape.models.login_request import LoginRequest
|
|
46
|
+
from entryscape.models.login_response import LoginResponse
|
|
47
|
+
from entryscape.models.metadata_format import MetadataFormat
|
|
48
|
+
from entryscape.models.metadata_request import MetadataRequest
|
|
49
|
+
from entryscape.models.parent_reference import ParentReference
|
|
50
|
+
from entryscape.models.search_request import SearchRequest
|
|
51
|
+
from entryscape.models.search_request_filters import SearchRequestFilters
|
|
52
|
+
from entryscape.models.search_request_filters_graph_type import (
|
|
53
|
+
SearchRequestFiltersGraphType,
|
|
54
|
+
)
|
|
55
|
+
from entryscape.models.search_request_filters_resource_type import (
|
|
56
|
+
SearchRequestFiltersResourceType,
|
|
57
|
+
)
|
|
58
|
+
from entryscape.models.search_request_sort import SearchRequestSort
|
|
59
|
+
from entryscape.models.search_request_sort_order import SearchRequestSortOrder
|
|
60
|
+
from entryscape.models.search_response import SearchResponse
|
|
61
|
+
from entryscape.models.search_result import SearchResult
|
|
62
|
+
from entryscape.models.search_result_graph_type import SearchResultGraphType
|
|
63
|
+
from entryscape.models.search_result_resource_type import SearchResultResourceType
|
|
64
|
+
from entryscape.models.search_sort_order_parameter import SearchSortOrderParameter
|
|
65
|
+
from entryscape.models.terminology_import_mode import TerminologyImportMode
|
|
66
|
+
from entryscape.models.validation_profile import ValidationProfile
|
|
67
|
+
from entryscape.models.validation_response import ValidationResponse
|
|
68
|
+
from entryscape.models.validation_result import ValidationResult
|
|
69
|
+
from entryscape.models.validation_severity import ValidationSeverity
|
|
70
|
+
from entryscape.models.validation_summary import ValidationSummary
|
|
71
|
+
from entryscape.models.whoami_response import WhoamiResponse
|
|
@@ -0,0 +1,156 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
EntryScape API
|
|
5
|
+
|
|
6
|
+
This API provides structured access to EntryScape data following DCAT-AP standards. All entity endpoints follow a consistent pattern: - GET /{entity} - List all entities - POST /{entity} - Create a new entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id} - Get entity reference info - DELETE /{entity}/{context_id}/{entry_id} - Delete an entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id}/metadata - Get raw RDF metadata - PUT /{entity}/{context_id}/{entry_id}/metadata - Replace raw RDF metadata (JSON-LD, Turtle, RDF/XML, or N-Triples passthrough) ## Reading and consistency Reads of a single entity — `GET /{entity}/{context_id}/{entry_id}` and its `/metadata` — go straight to the store and always reflect the latest write. Lists and search do not. They are answered from a search index that is updated asynchronously, so for a short interval after a write: - an entry that was just created may be missing from the list it belongs to, while reading it by id already works; - an entry that was just deleted may still appear; - `results` counts what the index holds, not what the store holds. Write a client that tolerates this: after creating an entry, use the id the create returned rather than searching for what you just wrote. `results` is also an upper bound rather than an exact count when the caller cannot read every hit (see the field's own description). Separately, responses are cached for 15 minutes by default, which is a second and unrelated source of staleness. A write invalidates the cache entries it affects, but a read that lands in the indexing gap can cache a list that does not yet show it. ## Authentication Most endpoints require an authenticated session. Reading public data works without authentication, while creating, updating, and deleting entities requires a valid user session. 1. **Log in** with `POST /auth/login`, supplying an EntryStore username and password. The response returns an `auth_token`. 2. **Send the token** on subsequent requests via the `X-Auth-Token` header. This is the recommended method for SDK and programmatic access. Browser clients can instead rely on the `auth_token` cookie, which the server sets automatically on login (`SameSite=Lax; Secure`). 3. **Inspect the session** with `GET /auth/whoami`, which returns the current user or an anonymous/guest identity when no valid token is supplied. 4. **Log out** with `POST /auth/logout` to invalidate the token and clear the cookie. ### Writes and CSRF Send the token as the `X-Auth-Token` **header** and there is nothing else to do: creates, updates and deletes work as they are, which is how the SDKs and the MCP server are built. The `auth_token` cookie is different. A cookie is attached by the browser automatically, so a state-changing request authenticated by it alone is what cross-site request forgery abuses, and those requests additionally need a double-submit token: read the `XSRF-TOKEN` cookie the server sets on any response and send its value back as the `X-XSRF-TOKEN` header. Without it the request is rejected with `403` before it reaches the endpoint. A custom header cannot be forged this way — cross-origin JavaScript cannot set one without a CORS preflight, which this API grants only to configured origins — so the header is the recommended path for anything that is not a browser session. ### App Tokens An app token identifies an integration. Create one with `POST /app-token` (unauthenticated). Creation requires an email address: the token starts in the `pending` state and a 6-digit code is emailed to the owner. Confirm the code with `POST /app-token/verify` to activate the token and receive its value, which is shown only once — store it securely. `GET /app-token/{app_token_id}` then returns its details and quota usage, authenticated by the token itself via the `X-App-Token` header. **The `X-App-Token` header is accepted only by `GET /app-token/{app_token_id}`.** No other operation reads it, and the read quota is recorded but not yet enforced: reading public data needs no token of any kind, and sending an app token with a read neither grants nor meters anything. Metered public read access is the intent of the two-token model, not a description of this release — see the `security` declared on each operation for what it actually accepts. ## SDKs Generated clients for Python, TypeScript, JavaScript and C# wrap every operation below, and each operation on this page shows the SDK call beside the `curl` command. Two of them install from a public registry: ``` pip install entryscape # Python npm install @entryscape/api-client # TypeScript and JavaScript ``` `@entryscape/api-client` ships an ESM and a CommonJS build, so `import` and `require()` both work and TypeScript is not required to use it — the name describes what it is written in, not what you have to write. There is a separate generated JavaScript SDK, and it is deliberately **not** on npm; this package is the npm client for both languages. Every SDK, including the C# and JavaScript ones, is also published as a tarball. The downloads are linked in the sidebar, they are the route to take when a registry is unreachable or a build has to vendor its dependencies, and each archive carries the SDK's own README: - **Python** — `pip install entryscape`, or `python-sdk.tar.gz`. - **TypeScript and JavaScript** — `npm install @entryscape/api-client`, or `typescript-sdk.tar.gz`. - **JavaScript (the separate generated client)** — not published to a registry, by design; `javascript-sdk.tar.gz` only. - **C#** — not published to NuGet yet; `csharp-sdk.tar.gz` only. - **MCP server** — `npx -y @entryscape/mcp-server`, or `mcp-server.tar.gz`. ## MCP Server A Model Context Protocol (MCP) server is generated from this specification and published alongside the SDKs (`@entryscape/mcp-server`), exposing the API as tools for AI assistants and agents. Each operation becomes one tool named after its `operationId` — `listDatasets`, `createCatalog`, `search`, `getJobStatus`. The `auth` operations are the exception: the server reads its token once at startup, so a `login` tool could install nothing and a `logout` tool would only invalidate the token the server was started with. On connect the server also hands the assistant a short set of rules that hold across every tool: a token is bound to one EntryStore instance, the search index lags a write, uploads finish as jobs. ### Install The server is on npm, and MCP servers are normally launched straight from there, so nothing has to be installed first: ``` npx -y @entryscape/mcp-server ``` `npm install -g @entryscape/mcp-server` works too and puts an `entryscape-mcp` binary on `PATH`. It is also listed in the MCP Registry as `com.entryscape/mcp-server`, which is where clients and marketplaces look it up. Without npm, download `mcp-server.tar.gz` from the SDK downloads and unpack it. The package ships prebuilt; install its runtime dependency once: ``` mkdir entryscape-mcp && tar -xzf mcp-server.tar.gz -C entryscape-mcp cd entryscape-mcp && npm install --omit=dev ``` ### Configure The server is a stdio MCP server configured through environment variables, read once at startup: - `ENTRYSCAPE_API_URL` — base URL of the API server, path included. Required: there is no default, and the server refuses to start without it. - `ENTRYSCAPE_AUTH_TOKEN` — a session token from `POST /auth/login`, sent as the `X-Auth-Token` header. Optional: without it reads see public entries and every write is refused. A token is valid only for the EntryStore instance that issued it. - `ENTRYSCAPE_ENTRYSTORE_HOST` — the EntryStore instance the API server should use, for example `dev.entryscape.com/store/`. Unset means the API server's own default. - `ENTRYSCAPE_MCP_UPLOAD_ROOT` — the directory the upload tools may read files from. **Unset means uploads are disabled**: `addFileToDistribution`, `replaceFileInDistribution` and `importTerminology` refuse and say so. - `ENTRYSCAPE_MCP_TAGS` — comma-separated tags to serve tools for, for example `catalog,dataset,distribution,search`. Unset serves every tool. The package's `README.md` lists the remaining variables (timeouts and size caps). Register the server with any MCP-capable client by adding it to the client's `mcpServers` configuration: ```json { \"mcpServers\": { \"entryscape\": { \"command\": \"npx\", \"args\": [\"-y\", \"@entryscape/mcp-server\"], \"env\": { \"ENTRYSCAPE_API_URL\": \"https://<your EntryScape API host>\", \"ENTRYSCAPE_AUTH_TOKEN\": \"your-session-token\" } } } } ``` From an unpacked download instead, the command is `\"command\": \"node\", \"args\": [\"/path/to/entryscape-mcp/dist/bin/entryscape-mcp.js\"]`. ### Claude Code The unpacked package is also a Claude Code plugin, so one install gives the server and the skills below together. Claude Code installs plugins from marketplaces, and the package carries a one-entry marketplace pointing at itself: ``` claude plugin marketplace add /path/to/entryscape-mcp claude plugin install entryscape@entryscape --scope user ``` The plugin passes `ENTRYSCAPE_API_URL`, `ENTRYSCAPE_AUTH_TOKEN`, `ENTRYSCAPE_ENTRYSTORE_HOST`, `ENTRYSCAPE_MCP_TAGS` and `ENTRYSCAPE_MCP_UPLOAD_ROOT` through from the shell Claude Code was started in, so set them there. To try the plugin for one session without installing it, start Claude Code with `--plugin-dir /path/to/entryscape-mcp`. ### Skills `skills/` in the package holds procedures that span several tools, in the Agent Skills format (a `SKILL.md` with `name` and `description`): which tools to call in which order, what to check between calls, and where to stop. Claude Code loads them from the plugin; other clients that read the format can be pointed at the directory. The `find` skill covers discovery, facets and selective metadata reads; `publish-dataset` takes a dataset from catalog to validated distribution with its file; `edit-metadata` changes an entry that already exists without dropping the rest of its graph, models included; `migrate-from-taskrunner` moves an integration off the deprecated Taskrunner API. Every tool a skill names is checked against this specification when the package is built. ## Deprecation Policy When endpoints or features are deprecated: 1. The operation is marked with `deprecated: true` in this spec 2. The operation description documents the replacement endpoint and sunset date 3. Deprecated endpoints remain functional for at least 6 months after announcement 4. The server returns a `Sunset` header with the planned removal date 5. After the sunset date, the endpoint may be removed in a future release
|
|
7
|
+
|
|
8
|
+
The version of the OpenAPI document: 1.3.0
|
|
9
|
+
Contact: contact@entryscape.com
|
|
10
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
11
|
+
|
|
12
|
+
Do not edit the class manually.
|
|
13
|
+
""" # noqa: E501
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
import pprint
|
|
17
|
+
import re # noqa: F401
|
|
18
|
+
import json
|
|
19
|
+
|
|
20
|
+
from datetime import datetime
|
|
21
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
22
|
+
from typing import Any, ClassVar, Dict, List, Optional
|
|
23
|
+
from typing_extensions import Annotated
|
|
24
|
+
from uuid import UUID
|
|
25
|
+
from entryscape.models.app_token_status import AppTokenStatus
|
|
26
|
+
from typing import Optional, Set
|
|
27
|
+
from typing_extensions import Self
|
|
28
|
+
from pydantic_core import to_jsonable_python
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class AppToken(BaseModel):
|
|
32
|
+
"""
|
|
33
|
+
App token details including quota usage. Returned by GET endpoint.
|
|
34
|
+
""" # noqa: E501
|
|
35
|
+
|
|
36
|
+
id: UUID = Field(
|
|
37
|
+
description="Unique identifier for an app token. Server-generated UUID."
|
|
38
|
+
)
|
|
39
|
+
status: AppTokenStatus
|
|
40
|
+
name: Annotated[str, Field(min_length=1, strict=True, max_length=256)] = Field(
|
|
41
|
+
description="Human-readable name for the token"
|
|
42
|
+
)
|
|
43
|
+
email: Annotated[str, Field(strict=True, max_length=320)] = Field(
|
|
44
|
+
description="Email address of the token owner"
|
|
45
|
+
)
|
|
46
|
+
created: datetime = Field(description="When the token was created")
|
|
47
|
+
expires_at: Optional[datetime] = Field(
|
|
48
|
+
default=None,
|
|
49
|
+
description="When the token expires. Null if the token does not expire.",
|
|
50
|
+
)
|
|
51
|
+
read_quota: Annotated[int, Field(strict=True, ge=0)] = Field(
|
|
52
|
+
description="Maximum number of reads allowed"
|
|
53
|
+
)
|
|
54
|
+
reads_used: Annotated[int, Field(strict=True, ge=0)] = Field(
|
|
55
|
+
description="Number of reads consumed so far"
|
|
56
|
+
)
|
|
57
|
+
__properties: ClassVar[List[str]] = [
|
|
58
|
+
"id",
|
|
59
|
+
"status",
|
|
60
|
+
"name",
|
|
61
|
+
"email",
|
|
62
|
+
"created",
|
|
63
|
+
"expires_at",
|
|
64
|
+
"read_quota",
|
|
65
|
+
"reads_used",
|
|
66
|
+
]
|
|
67
|
+
|
|
68
|
+
model_config = ConfigDict(
|
|
69
|
+
validate_by_name=True,
|
|
70
|
+
validate_by_alias=True,
|
|
71
|
+
validate_assignment=True,
|
|
72
|
+
protected_namespaces=(),
|
|
73
|
+
)
|
|
74
|
+
|
|
75
|
+
def to_str(self) -> str:
|
|
76
|
+
"""Returns the string representation of the model using alias"""
|
|
77
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
|
78
|
+
|
|
79
|
+
def to_json(self) -> str:
|
|
80
|
+
"""Returns the JSON representation of the model using alias"""
|
|
81
|
+
return json.dumps(to_jsonable_python(self.to_dict()))
|
|
82
|
+
|
|
83
|
+
@classmethod
|
|
84
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
|
85
|
+
"""Create an instance of AppToken from a JSON string"""
|
|
86
|
+
return cls.from_dict(json.loads(json_str))
|
|
87
|
+
|
|
88
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
89
|
+
"""Return the dictionary representation of the model using alias.
|
|
90
|
+
|
|
91
|
+
This has the following differences from calling pydantic's
|
|
92
|
+
`self.model_dump(by_alias=True)`:
|
|
93
|
+
|
|
94
|
+
* `None` is only added to the output dict for nullable fields that
|
|
95
|
+
were set at model initialization. Other fields with value `None`
|
|
96
|
+
are ignored.
|
|
97
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
98
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
99
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
100
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
101
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
102
|
+
* OpenAPI `readOnly` fields are excluded.
|
|
103
|
+
"""
|
|
104
|
+
excluded_fields: Set[str] = set(
|
|
105
|
+
[
|
|
106
|
+
"name",
|
|
107
|
+
"email",
|
|
108
|
+
"created",
|
|
109
|
+
"expires_at",
|
|
110
|
+
"read_quota",
|
|
111
|
+
"reads_used",
|
|
112
|
+
]
|
|
113
|
+
)
|
|
114
|
+
|
|
115
|
+
_dict = self.model_dump(
|
|
116
|
+
by_alias=True,
|
|
117
|
+
exclude=excluded_fields,
|
|
118
|
+
exclude_none=True,
|
|
119
|
+
)
|
|
120
|
+
# set to None if expires_at (nullable) is None
|
|
121
|
+
# and model_fields_set contains the field
|
|
122
|
+
if self.expires_at is None and "expires_at" in self.model_fields_set:
|
|
123
|
+
_dict["expires_at"] = None
|
|
124
|
+
|
|
125
|
+
return _dict
|
|
126
|
+
|
|
127
|
+
@classmethod
|
|
128
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
|
129
|
+
"""Create an instance of AppToken from a dict"""
|
|
130
|
+
if obj is None:
|
|
131
|
+
return None
|
|
132
|
+
|
|
133
|
+
if not isinstance(obj, dict):
|
|
134
|
+
return cls.model_validate(obj)
|
|
135
|
+
|
|
136
|
+
# raise errors for additional fields in the input
|
|
137
|
+
for _key in obj.keys():
|
|
138
|
+
if _key not in cls.__properties:
|
|
139
|
+
raise ValueError(
|
|
140
|
+
"Error due to additional fields (not defined in AppToken) in the input: "
|
|
141
|
+
+ _key
|
|
142
|
+
)
|
|
143
|
+
|
|
144
|
+
_obj = cls.model_validate(
|
|
145
|
+
{
|
|
146
|
+
"id": obj.get("id"),
|
|
147
|
+
"status": obj.get("status"),
|
|
148
|
+
"name": obj.get("name"),
|
|
149
|
+
"email": obj.get("email"),
|
|
150
|
+
"created": obj.get("created"),
|
|
151
|
+
"expires_at": obj.get("expires_at"),
|
|
152
|
+
"read_quota": obj.get("read_quota"),
|
|
153
|
+
"reads_used": obj.get("reads_used"),
|
|
154
|
+
}
|
|
155
|
+
)
|
|
156
|
+
return _obj
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
|
|
3
|
+
"""
|
|
4
|
+
EntryScape API
|
|
5
|
+
|
|
6
|
+
This API provides structured access to EntryScape data following DCAT-AP standards. All entity endpoints follow a consistent pattern: - GET /{entity} - List all entities - POST /{entity} - Create a new entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id} - Get entity reference info - DELETE /{entity}/{context_id}/{entry_id} - Delete an entity (DCAT types only) - GET /{entity}/{context_id}/{entry_id}/metadata - Get raw RDF metadata - PUT /{entity}/{context_id}/{entry_id}/metadata - Replace raw RDF metadata (JSON-LD, Turtle, RDF/XML, or N-Triples passthrough) ## Reading and consistency Reads of a single entity — `GET /{entity}/{context_id}/{entry_id}` and its `/metadata` — go straight to the store and always reflect the latest write. Lists and search do not. They are answered from a search index that is updated asynchronously, so for a short interval after a write: - an entry that was just created may be missing from the list it belongs to, while reading it by id already works; - an entry that was just deleted may still appear; - `results` counts what the index holds, not what the store holds. Write a client that tolerates this: after creating an entry, use the id the create returned rather than searching for what you just wrote. `results` is also an upper bound rather than an exact count when the caller cannot read every hit (see the field's own description). Separately, responses are cached for 15 minutes by default, which is a second and unrelated source of staleness. A write invalidates the cache entries it affects, but a read that lands in the indexing gap can cache a list that does not yet show it. ## Authentication Most endpoints require an authenticated session. Reading public data works without authentication, while creating, updating, and deleting entities requires a valid user session. 1. **Log in** with `POST /auth/login`, supplying an EntryStore username and password. The response returns an `auth_token`. 2. **Send the token** on subsequent requests via the `X-Auth-Token` header. This is the recommended method for SDK and programmatic access. Browser clients can instead rely on the `auth_token` cookie, which the server sets automatically on login (`SameSite=Lax; Secure`). 3. **Inspect the session** with `GET /auth/whoami`, which returns the current user or an anonymous/guest identity when no valid token is supplied. 4. **Log out** with `POST /auth/logout` to invalidate the token and clear the cookie. ### Writes and CSRF Send the token as the `X-Auth-Token` **header** and there is nothing else to do: creates, updates and deletes work as they are, which is how the SDKs and the MCP server are built. The `auth_token` cookie is different. A cookie is attached by the browser automatically, so a state-changing request authenticated by it alone is what cross-site request forgery abuses, and those requests additionally need a double-submit token: read the `XSRF-TOKEN` cookie the server sets on any response and send its value back as the `X-XSRF-TOKEN` header. Without it the request is rejected with `403` before it reaches the endpoint. A custom header cannot be forged this way — cross-origin JavaScript cannot set one without a CORS preflight, which this API grants only to configured origins — so the header is the recommended path for anything that is not a browser session. ### App Tokens An app token identifies an integration. Create one with `POST /app-token` (unauthenticated). Creation requires an email address: the token starts in the `pending` state and a 6-digit code is emailed to the owner. Confirm the code with `POST /app-token/verify` to activate the token and receive its value, which is shown only once — store it securely. `GET /app-token/{app_token_id}` then returns its details and quota usage, authenticated by the token itself via the `X-App-Token` header. **The `X-App-Token` header is accepted only by `GET /app-token/{app_token_id}`.** No other operation reads it, and the read quota is recorded but not yet enforced: reading public data needs no token of any kind, and sending an app token with a read neither grants nor meters anything. Metered public read access is the intent of the two-token model, not a description of this release — see the `security` declared on each operation for what it actually accepts. ## SDKs Generated clients for Python, TypeScript, JavaScript and C# wrap every operation below, and each operation on this page shows the SDK call beside the `curl` command. Two of them install from a public registry: ``` pip install entryscape # Python npm install @entryscape/api-client # TypeScript and JavaScript ``` `@entryscape/api-client` ships an ESM and a CommonJS build, so `import` and `require()` both work and TypeScript is not required to use it — the name describes what it is written in, not what you have to write. There is a separate generated JavaScript SDK, and it is deliberately **not** on npm; this package is the npm client for both languages. Every SDK, including the C# and JavaScript ones, is also published as a tarball. The downloads are linked in the sidebar, they are the route to take when a registry is unreachable or a build has to vendor its dependencies, and each archive carries the SDK's own README: - **Python** — `pip install entryscape`, or `python-sdk.tar.gz`. - **TypeScript and JavaScript** — `npm install @entryscape/api-client`, or `typescript-sdk.tar.gz`. - **JavaScript (the separate generated client)** — not published to a registry, by design; `javascript-sdk.tar.gz` only. - **C#** — not published to NuGet yet; `csharp-sdk.tar.gz` only. - **MCP server** — `npx -y @entryscape/mcp-server`, or `mcp-server.tar.gz`. ## MCP Server A Model Context Protocol (MCP) server is generated from this specification and published alongside the SDKs (`@entryscape/mcp-server`), exposing the API as tools for AI assistants and agents. Each operation becomes one tool named after its `operationId` — `listDatasets`, `createCatalog`, `search`, `getJobStatus`. The `auth` operations are the exception: the server reads its token once at startup, so a `login` tool could install nothing and a `logout` tool would only invalidate the token the server was started with. On connect the server also hands the assistant a short set of rules that hold across every tool: a token is bound to one EntryStore instance, the search index lags a write, uploads finish as jobs. ### Install The server is on npm, and MCP servers are normally launched straight from there, so nothing has to be installed first: ``` npx -y @entryscape/mcp-server ``` `npm install -g @entryscape/mcp-server` works too and puts an `entryscape-mcp` binary on `PATH`. It is also listed in the MCP Registry as `com.entryscape/mcp-server`, which is where clients and marketplaces look it up. Without npm, download `mcp-server.tar.gz` from the SDK downloads and unpack it. The package ships prebuilt; install its runtime dependency once: ``` mkdir entryscape-mcp && tar -xzf mcp-server.tar.gz -C entryscape-mcp cd entryscape-mcp && npm install --omit=dev ``` ### Configure The server is a stdio MCP server configured through environment variables, read once at startup: - `ENTRYSCAPE_API_URL` — base URL of the API server, path included. Required: there is no default, and the server refuses to start without it. - `ENTRYSCAPE_AUTH_TOKEN` — a session token from `POST /auth/login`, sent as the `X-Auth-Token` header. Optional: without it reads see public entries and every write is refused. A token is valid only for the EntryStore instance that issued it. - `ENTRYSCAPE_ENTRYSTORE_HOST` — the EntryStore instance the API server should use, for example `dev.entryscape.com/store/`. Unset means the API server's own default. - `ENTRYSCAPE_MCP_UPLOAD_ROOT` — the directory the upload tools may read files from. **Unset means uploads are disabled**: `addFileToDistribution`, `replaceFileInDistribution` and `importTerminology` refuse and say so. - `ENTRYSCAPE_MCP_TAGS` — comma-separated tags to serve tools for, for example `catalog,dataset,distribution,search`. Unset serves every tool. The package's `README.md` lists the remaining variables (timeouts and size caps). Register the server with any MCP-capable client by adding it to the client's `mcpServers` configuration: ```json { \"mcpServers\": { \"entryscape\": { \"command\": \"npx\", \"args\": [\"-y\", \"@entryscape/mcp-server\"], \"env\": { \"ENTRYSCAPE_API_URL\": \"https://<your EntryScape API host>\", \"ENTRYSCAPE_AUTH_TOKEN\": \"your-session-token\" } } } } ``` From an unpacked download instead, the command is `\"command\": \"node\", \"args\": [\"/path/to/entryscape-mcp/dist/bin/entryscape-mcp.js\"]`. ### Claude Code The unpacked package is also a Claude Code plugin, so one install gives the server and the skills below together. Claude Code installs plugins from marketplaces, and the package carries a one-entry marketplace pointing at itself: ``` claude plugin marketplace add /path/to/entryscape-mcp claude plugin install entryscape@entryscape --scope user ``` The plugin passes `ENTRYSCAPE_API_URL`, `ENTRYSCAPE_AUTH_TOKEN`, `ENTRYSCAPE_ENTRYSTORE_HOST`, `ENTRYSCAPE_MCP_TAGS` and `ENTRYSCAPE_MCP_UPLOAD_ROOT` through from the shell Claude Code was started in, so set them there. To try the plugin for one session without installing it, start Claude Code with `--plugin-dir /path/to/entryscape-mcp`. ### Skills `skills/` in the package holds procedures that span several tools, in the Agent Skills format (a `SKILL.md` with `name` and `description`): which tools to call in which order, what to check between calls, and where to stop. Claude Code loads them from the plugin; other clients that read the format can be pointed at the directory. The `find` skill covers discovery, facets and selective metadata reads; `publish-dataset` takes a dataset from catalog to validated distribution with its file; `edit-metadata` changes an entry that already exists without dropping the rest of its graph, models included; `migrate-from-taskrunner` moves an integration off the deprecated Taskrunner API. Every tool a skill names is checked against this specification when the package is built. ## Deprecation Policy When endpoints or features are deprecated: 1. The operation is marked with `deprecated: true` in this spec 2. The operation description documents the replacement endpoint and sunset date 3. Deprecated endpoints remain functional for at least 6 months after announcement 4. The server returns a `Sunset` header with the planned removal date 5. After the sunset date, the endpoint may be removed in a future release
|
|
7
|
+
|
|
8
|
+
The version of the OpenAPI document: 1.3.0
|
|
9
|
+
Contact: contact@entryscape.com
|
|
10
|
+
Generated by OpenAPI Generator (https://openapi-generator.tech)
|
|
11
|
+
|
|
12
|
+
Do not edit the class manually.
|
|
13
|
+
""" # noqa: E501
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
import pprint
|
|
17
|
+
import re # noqa: F401
|
|
18
|
+
import json
|
|
19
|
+
|
|
20
|
+
from pydantic import BaseModel, ConfigDict, Field
|
|
21
|
+
from typing import Any, ClassVar, Dict, List
|
|
22
|
+
from typing_extensions import Annotated
|
|
23
|
+
from typing import Optional, Set
|
|
24
|
+
from typing_extensions import Self
|
|
25
|
+
from pydantic_core import to_jsonable_python
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
class AppTokenCreateRequest(BaseModel):
|
|
29
|
+
"""
|
|
30
|
+
Request body for creating a new app token.
|
|
31
|
+
""" # noqa: E501
|
|
32
|
+
|
|
33
|
+
name: Annotated[str, Field(min_length=1, strict=True, max_length=256)] = Field(
|
|
34
|
+
description="Human-readable name for the token"
|
|
35
|
+
)
|
|
36
|
+
email: Annotated[str, Field(strict=True, max_length=320)] = Field(
|
|
37
|
+
description="Email address of the token owner. A 6-digit verification code is sent to this address; the token value is only issued once the code is confirmed via `POST /app-token/verify`. "
|
|
38
|
+
)
|
|
39
|
+
__properties: ClassVar[List[str]] = ["name", "email"]
|
|
40
|
+
|
|
41
|
+
model_config = ConfigDict(
|
|
42
|
+
validate_by_name=True,
|
|
43
|
+
validate_by_alias=True,
|
|
44
|
+
validate_assignment=True,
|
|
45
|
+
protected_namespaces=(),
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
def to_str(self) -> str:
|
|
49
|
+
"""Returns the string representation of the model using alias"""
|
|
50
|
+
return pprint.pformat(self.model_dump(by_alias=True))
|
|
51
|
+
|
|
52
|
+
def to_json(self) -> str:
|
|
53
|
+
"""Returns the JSON representation of the model using alias"""
|
|
54
|
+
return json.dumps(to_jsonable_python(self.to_dict()))
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def from_json(cls, json_str: str) -> Optional[Self]:
|
|
58
|
+
"""Create an instance of AppTokenCreateRequest from a JSON string"""
|
|
59
|
+
return cls.from_dict(json.loads(json_str))
|
|
60
|
+
|
|
61
|
+
def to_dict(self) -> Dict[str, Any]:
|
|
62
|
+
"""Return the dictionary representation of the model using alias.
|
|
63
|
+
|
|
64
|
+
This has the following differences from calling pydantic's
|
|
65
|
+
`self.model_dump(by_alias=True)`:
|
|
66
|
+
|
|
67
|
+
* `None` is only added to the output dict for nullable fields that
|
|
68
|
+
were set at model initialization. Other fields with value `None`
|
|
69
|
+
are ignored.
|
|
70
|
+
"""
|
|
71
|
+
excluded_fields: Set[str] = set([])
|
|
72
|
+
|
|
73
|
+
_dict = self.model_dump(
|
|
74
|
+
by_alias=True,
|
|
75
|
+
exclude=excluded_fields,
|
|
76
|
+
exclude_none=True,
|
|
77
|
+
)
|
|
78
|
+
return _dict
|
|
79
|
+
|
|
80
|
+
@classmethod
|
|
81
|
+
def from_dict(cls, obj: Optional[Dict[str, Any]]) -> Optional[Self]:
|
|
82
|
+
"""Create an instance of AppTokenCreateRequest from a dict"""
|
|
83
|
+
if obj is None:
|
|
84
|
+
return None
|
|
85
|
+
|
|
86
|
+
if not isinstance(obj, dict):
|
|
87
|
+
return cls.model_validate(obj)
|
|
88
|
+
|
|
89
|
+
# raise errors for additional fields in the input
|
|
90
|
+
for _key in obj.keys():
|
|
91
|
+
if _key not in cls.__properties:
|
|
92
|
+
raise ValueError(
|
|
93
|
+
"Error due to additional fields (not defined in AppTokenCreateRequest) in the input: "
|
|
94
|
+
+ _key
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
_obj = cls.model_validate({"name": obj.get("name"), "email": obj.get("email")})
|
|
98
|
+
return _obj
|