@toa.io/extensions.exposition 1.0.0-alpha.286 → 1.0.0-alpha.288
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.
- package/CHANGELOG.md +16 -0
- package/components/identity.basic/tsconfig.tsbuildinfo +1 -1
- package/components/identity.clients/tsconfig.tsbuildinfo +1 -1
- package/components/identity.credentials/tsconfig.tsbuildinfo +1 -1
- package/components/identity.federation/tsconfig.tsbuildinfo +1 -1
- package/components/identity.grants/tsconfig.tsbuildinfo +1 -1
- package/components/identity.keys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.otp/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/manifest.toa.yaml +2 -1
- package/components/identity.passkeys/source/errors.test.ts +34 -0
- package/components/identity.passkeys/source/webauthn.test.ts +26 -0
- package/components/identity.passkeys/tsconfig.tsbuildinfo +1 -1
- package/components/identity.passkeys/types/toa.d.ts +1 -1
- package/components/identity.tokens/tsconfig.tsbuildinfo +1 -1
- package/documentation/access.md +22 -4
- package/documentation/discovery.md +81 -0
- package/documentation/help.md +109 -0
- package/documentation/introspection.md +34 -9
- package/documentation/mcp.md +13 -16
- package/features/cors.feature +31 -1
- package/features/dev.feature +2 -0
- package/features/discovery.feature +211 -0
- package/features/discovery.ui.feature +197 -0
- package/features/help.feature +466 -0
- package/features/introspection.feature +94 -8
- package/features/map.feature +1 -0
- package/features/mcp.feature +74 -12
- package/features/methods.feature +2 -1
- package/features/oauth.grants.feature +3 -1
- package/features/octets.download.feature +1 -0
- package/features/octets.meta.feature +1 -0
- package/features/site/_app/immutable/asset.js +1 -0
- package/features/site/favicon.ico +0 -0
- package/features/site/index.html +10 -0
- package/features/steps/Parameters.ts +12 -0
- package/package.json +6 -4
- package/readme.md +3 -0
- package/source/Directive.ts +4 -0
- package/source/Discovery/Explorer.ts +34 -0
- package/source/Discovery/Site.test.ts +147 -0
- package/source/Discovery/Site.ts +202 -0
- package/source/Discovery/index.ts +3 -0
- package/source/Discovery/tree.test.ts +175 -0
- package/source/Discovery/tree.ts +77 -0
- package/source/Discovery/trunk.ts +23 -0
- package/source/Endpoint.ts +4 -0
- package/source/Gateway.ts +34 -4
- package/source/HTTP/Context.ts +7 -0
- package/source/Introspection.ts +33 -10
- package/source/MCP/schema.ts +16 -5
- package/source/MCP/tools.ts +14 -32
- package/source/Mapping.ts +5 -0
- package/source/Query.ts +17 -2
- package/source/RTD/Directives.ts +10 -0
- package/source/RTD/Endpoint.ts +8 -1
- package/source/RTD/Node.ts +20 -6
- package/source/RTD/Tree.ts +19 -9
- package/source/RTD/factory.ts +37 -1
- package/source/RTD/segment.ts +21 -0
- package/source/const.ts +8 -0
- package/source/directives/auth/Anonymous.test.ts +31 -5
- package/source/directives/auth/Anonymous.ts +5 -3
- package/source/directives/auth/Anyone.ts +6 -0
- package/source/directives/auth/Assert.ts +5 -0
- package/source/directives/auth/Delegate.ts +5 -2
- package/source/directives/auth/Federation.ts +11 -0
- package/source/directives/auth/Id.ts +15 -0
- package/source/directives/auth/Input.ts +5 -0
- package/source/directives/auth/Role.ts +17 -0
- package/source/directives/cors/CORS.test.ts +75 -0
- package/source/directives/cors/CORS.ts +10 -3
- package/source/directives/help/Family.ts +118 -0
- package/source/directives/help/Help.test.ts +227 -0
- package/source/directives/help/Help.ts +26 -0
- package/source/directives/help/Parameters.ts +47 -0
- package/source/directives/help/described.ts +57 -0
- package/source/directives/help/index.ts +45 -0
- package/source/directives/index.ts +8 -1
- package/source/directives/map/Headers.ts +6 -6
- package/source/directives/mcp/MCP.ts +12 -21
- package/source/directives/mcp/Tool.test.ts +22 -68
- package/source/directives/mcp/Tool.ts +14 -41
- package/transpiled/Directive.d.ts +1 -0
- package/transpiled/Directive.js +3 -0
- package/transpiled/Directive.js.map +1 -1
- package/transpiled/Discovery/Explorer.d.ts +11 -0
- package/transpiled/Discovery/Explorer.js +29 -0
- package/transpiled/Discovery/Explorer.js.map +1 -0
- package/transpiled/Discovery/Site.d.ts +43 -0
- package/transpiled/Discovery/Site.js +171 -0
- package/transpiled/Discovery/Site.js.map +1 -0
- package/transpiled/Discovery/index.d.ts +3 -0
- package/transpiled/Discovery/index.js +4 -0
- package/transpiled/Discovery/index.js.map +1 -0
- package/transpiled/Discovery/tree.d.ts +26 -0
- package/transpiled/Discovery/tree.js +50 -0
- package/transpiled/Discovery/tree.js.map +1 -0
- package/transpiled/Discovery/trunk.d.ts +10 -0
- package/transpiled/Discovery/trunk.js +21 -0
- package/transpiled/Discovery/trunk.js.map +1 -0
- package/transpiled/Endpoint.d.ts +2 -1
- package/transpiled/Endpoint.js +3 -0
- package/transpiled/Endpoint.js.map +1 -1
- package/transpiled/Gateway.d.ts +4 -0
- package/transpiled/Gateway.js +29 -4
- package/transpiled/Gateway.js.map +1 -1
- package/transpiled/HTTP/Context.d.ts +6 -0
- package/transpiled/HTTP/Context.js +6 -0
- package/transpiled/HTTP/Context.js.map +1 -1
- package/transpiled/Introspection.d.ts +18 -7
- package/transpiled/Introspection.js +16 -1
- package/transpiled/Introspection.js.map +1 -1
- package/transpiled/MCP/schema.d.ts +2 -2
- package/transpiled/MCP/schema.js +11 -5
- package/transpiled/MCP/schema.js.map +1 -1
- package/transpiled/MCP/tools.d.ts +1 -1
- package/transpiled/MCP/tools.js +12 -28
- package/transpiled/MCP/tools.js.map +1 -1
- package/transpiled/Mapping.d.ts +2 -0
- package/transpiled/Mapping.js +4 -0
- package/transpiled/Mapping.js.map +1 -1
- package/transpiled/Query.d.ts +12 -0
- package/transpiled/Query.js +16 -2
- package/transpiled/Query.js.map +1 -1
- package/transpiled/RTD/Directives.d.ts +8 -0
- package/transpiled/RTD/Endpoint.d.ts +7 -1
- package/transpiled/RTD/Node.d.ts +11 -2
- package/transpiled/RTD/Node.js +12 -2
- package/transpiled/RTD/Node.js.map +1 -1
- package/transpiled/RTD/Tree.js +15 -8
- package/transpiled/RTD/Tree.js.map +1 -1
- package/transpiled/RTD/factory.js +27 -1
- package/transpiled/RTD/factory.js.map +1 -1
- package/transpiled/RTD/segment.d.ts +7 -0
- package/transpiled/RTD/segment.js +17 -0
- package/transpiled/RTD/segment.js.map +1 -1
- package/transpiled/const.d.ts +7 -0
- package/transpiled/const.js +7 -0
- package/transpiled/const.js.map +1 -1
- package/transpiled/directives/auth/Anonymous.d.ts +4 -2
- package/transpiled/directives/auth/Anonymous.js +5 -3
- package/transpiled/directives/auth/Anonymous.js.map +1 -1
- package/transpiled/directives/auth/Anyone.d.ts +3 -0
- package/transpiled/directives/auth/Anyone.js +4 -0
- package/transpiled/directives/auth/Anyone.js.map +1 -1
- package/transpiled/directives/auth/Assert.d.ts +2 -0
- package/transpiled/directives/auth/Assert.js +4 -0
- package/transpiled/directives/auth/Assert.js.map +1 -1
- package/transpiled/directives/auth/Delegate.d.ts +4 -1
- package/transpiled/directives/auth/Delegate.js +5 -2
- package/transpiled/directives/auth/Delegate.js.map +1 -1
- package/transpiled/directives/auth/Federation.d.ts +5 -0
- package/transpiled/directives/auth/Federation.js +8 -0
- package/transpiled/directives/auth/Federation.js.map +1 -1
- package/transpiled/directives/auth/Id.d.ts +9 -0
- package/transpiled/directives/auth/Id.js +12 -0
- package/transpiled/directives/auth/Id.js.map +1 -1
- package/transpiled/directives/auth/Input.d.ts +2 -0
- package/transpiled/directives/auth/Input.js +4 -0
- package/transpiled/directives/auth/Input.js.map +1 -1
- package/transpiled/directives/auth/Role.d.ts +7 -0
- package/transpiled/directives/auth/Role.js +11 -0
- package/transpiled/directives/auth/Role.js.map +1 -1
- package/transpiled/directives/cors/CORS.js +9 -2
- package/transpiled/directives/cors/CORS.js.map +1 -1
- package/transpiled/directives/help/Family.d.ts +28 -0
- package/transpiled/directives/help/Family.js +79 -0
- package/transpiled/directives/help/Family.js.map +1 -0
- package/transpiled/directives/help/Help.d.ts +16 -0
- package/transpiled/directives/help/Help.js +22 -0
- package/transpiled/directives/help/Help.js.map +1 -0
- package/transpiled/directives/help/Parameters.d.ts +18 -0
- package/transpiled/directives/help/Parameters.js +34 -0
- package/transpiled/directives/help/Parameters.js.map +1 -0
- package/transpiled/directives/help/described.d.ts +16 -0
- package/transpiled/directives/help/described.js +23 -0
- package/transpiled/directives/help/described.js.map +1 -0
- package/transpiled/directives/help/index.d.ts +22 -0
- package/transpiled/directives/help/index.js +34 -0
- package/transpiled/directives/help/index.js.map +1 -0
- package/transpiled/directives/index.d.ts +4 -0
- package/transpiled/directives/index.js +8 -1
- package/transpiled/directives/index.js.map +1 -1
- package/transpiled/directives/map/Headers.d.ts +5 -0
- package/transpiled/directives/map/Headers.js +7 -5
- package/transpiled/directives/map/Headers.js.map +1 -1
- package/transpiled/directives/mcp/MCP.d.ts +8 -5
- package/transpiled/directives/mcp/MCP.js +9 -13
- package/transpiled/directives/mcp/MCP.js.map +1 -1
- package/transpiled/directives/mcp/Tool.d.ts +6 -12
- package/transpiled/directives/mcp/Tool.js +10 -23
- package/transpiled/directives/mcp/Tool.js.map +1 -1
- package/tsconfig.tsbuildinfo +1 -1
- package/ui/dist/_app/env.js +1 -0
- package/ui/dist/_app/immutable/assets/0.BcIGtTfw.css +2 -0
- package/ui/dist/_app/immutable/assets/2.BC0-Jhqu.css +1 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-ext-wght-normal.BOeWTOD4.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-cyrillic-wght-normal.DqGufNeO.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-ext-wght-normal.DlzME5K_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-greek-wght-normal.CkhJZR-_.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-ext-wght-normal.DO1Apj_S.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-latin-wght-normal.Dx4kXJAl.woff2 +0 -0
- package/ui/dist/_app/immutable/assets/inter-vietnamese-wght-normal.CBcvBZtf.woff2 +0 -0
- package/ui/dist/_app/immutable/chunks/BgTrzN6u.js +1 -0
- package/ui/dist/_app/immutable/chunks/Bjy-W4x2.js +81 -0
- package/ui/dist/_app/immutable/chunks/CWJaG9mK.js +6 -0
- package/ui/dist/_app/immutable/chunks/DjIYL2zC.js +3 -0
- package/ui/dist/_app/immutable/chunks/xihTtKlq.js +1 -0
- package/ui/dist/_app/immutable/chunks/yDQhitF9.js +1 -0
- package/ui/dist/_app/immutable/entry/app.WZSMBcGf.js +2 -0
- package/ui/dist/_app/immutable/entry/start._LkoC6Lz.js +1 -0
- package/ui/dist/_app/immutable/nodes/0.Dl2DqINo.js +1 -0
- package/ui/dist/_app/immutable/nodes/1.CSSnGhVD.js +1 -0
- package/ui/dist/_app/immutable/nodes/2.DmolQyNG.js +14 -0
- package/ui/dist/_app/version.json +1 -0
- package/ui/dist/apple-touch-icon.png +0 -0
- package/ui/dist/favicon-96x96.png +0 -0
- package/ui/dist/favicon.ico +0 -0
- package/ui/dist/index.html +66 -0
|
@@ -0,0 +1,197 @@
|
|
|
1
|
+
Feature: The discovery page
|
|
2
|
+
|
|
3
|
+
The page that reads the tree, served from the same origin the tree is. The scenarios are
|
|
4
|
+
about the server rather than the page: it is pointed at a fixture, so none of them needs
|
|
5
|
+
a UI build.
|
|
6
|
+
|
|
7
|
+
Scenario: The page
|
|
8
|
+
When the following request is received:
|
|
9
|
+
"""
|
|
10
|
+
GET /.discovery/ HTTP/1.1
|
|
11
|
+
host: nex.toa.io
|
|
12
|
+
"""
|
|
13
|
+
Then the following reply is sent:
|
|
14
|
+
"""
|
|
15
|
+
200 OK
|
|
16
|
+
cache-control: no-cache
|
|
17
|
+
content-type: text/html; charset=utf-8
|
|
18
|
+
|
|
19
|
+
the page
|
|
20
|
+
"""
|
|
21
|
+
|
|
22
|
+
Scenario: The bare path is the directory it is
|
|
23
|
+
When the following request is received:
|
|
24
|
+
"""
|
|
25
|
+
GET /.discovery HTTP/1.1
|
|
26
|
+
host: nex.toa.io
|
|
27
|
+
"""
|
|
28
|
+
Then the following reply is sent:
|
|
29
|
+
"""
|
|
30
|
+
302 Moved Temporarily
|
|
31
|
+
location: /.discovery/
|
|
32
|
+
"""
|
|
33
|
+
|
|
34
|
+
Scenario: An asset carries the build it was named for
|
|
35
|
+
When the following request is received:
|
|
36
|
+
"""
|
|
37
|
+
GET /.discovery/_app/immutable/asset.js HTTP/1.1
|
|
38
|
+
host: nex.toa.io
|
|
39
|
+
"""
|
|
40
|
+
Then the following reply is sent:
|
|
41
|
+
"""
|
|
42
|
+
200 OK
|
|
43
|
+
cache-control: public, max-age=31536000, immutable
|
|
44
|
+
content-type: text/javascript; charset=utf-8
|
|
45
|
+
"""
|
|
46
|
+
|
|
47
|
+
Scenario: Anything that could be a route is the page
|
|
48
|
+
The client router knows what routes there are, and this server does not.
|
|
49
|
+
|
|
50
|
+
When the following request is received:
|
|
51
|
+
"""
|
|
52
|
+
GET /.discovery/pots/:id HTTP/1.1
|
|
53
|
+
host: nex.toa.io
|
|
54
|
+
"""
|
|
55
|
+
Then the following reply is sent:
|
|
56
|
+
"""
|
|
57
|
+
200 OK
|
|
58
|
+
content-type: text/html; charset=utf-8
|
|
59
|
+
|
|
60
|
+
the page
|
|
61
|
+
"""
|
|
62
|
+
|
|
63
|
+
Scenario: An asset that is not there is not there
|
|
64
|
+
When the following request is received:
|
|
65
|
+
"""
|
|
66
|
+
GET /.discovery/_app/immutable/gone.js HTTP/1.1
|
|
67
|
+
host: nex.toa.io
|
|
68
|
+
"""
|
|
69
|
+
Then the following reply is sent:
|
|
70
|
+
"""
|
|
71
|
+
404 Not Found
|
|
72
|
+
"""
|
|
73
|
+
|
|
74
|
+
Scenario: A path that merely starts like this one is not this one
|
|
75
|
+
When the following request is received:
|
|
76
|
+
"""
|
|
77
|
+
GET /.discoveryable/ HTTP/1.1
|
|
78
|
+
host: nex.toa.io
|
|
79
|
+
"""
|
|
80
|
+
Then the following reply is sent:
|
|
81
|
+
"""
|
|
82
|
+
404 Not Found
|
|
83
|
+
"""
|
|
84
|
+
|
|
85
|
+
Scenario: A path that climbs out of the page is refused
|
|
86
|
+
An encoded separator is what reaches here: a dot segment is collapsed while the URL is
|
|
87
|
+
parsed, and `%2f` survives that to decode into one afterwards.
|
|
88
|
+
|
|
89
|
+
When the following request is received:
|
|
90
|
+
"""
|
|
91
|
+
GET /.discovery/%2f%2e%2e%2f%2e%2e%2fpackage.json HTTP/1.1
|
|
92
|
+
host: nex.toa.io
|
|
93
|
+
"""
|
|
94
|
+
Then the following reply is sent:
|
|
95
|
+
"""
|
|
96
|
+
404 Not Found
|
|
97
|
+
"""
|
|
98
|
+
|
|
99
|
+
Scenario: The page is served before a credential is read
|
|
100
|
+
A page is public, and one served after `auth` would refuse the client holding a stale
|
|
101
|
+
token — who is the client most likely to have opened it.
|
|
102
|
+
|
|
103
|
+
Given the annotation:
|
|
104
|
+
"""yaml
|
|
105
|
+
/:
|
|
106
|
+
GET:
|
|
107
|
+
dev:stub: hello
|
|
108
|
+
"""
|
|
109
|
+
When the following request is received:
|
|
110
|
+
"""
|
|
111
|
+
GET / HTTP/1.1
|
|
112
|
+
host: nex.toa.io
|
|
113
|
+
"""
|
|
114
|
+
Then the following reply is sent:
|
|
115
|
+
"""
|
|
116
|
+
401 Unauthorized
|
|
117
|
+
"""
|
|
118
|
+
When the following request is received:
|
|
119
|
+
"""
|
|
120
|
+
GET /.discovery/ HTTP/1.1
|
|
121
|
+
host: nex.toa.io
|
|
122
|
+
"""
|
|
123
|
+
Then the following reply is sent:
|
|
124
|
+
"""
|
|
125
|
+
200 OK
|
|
126
|
+
|
|
127
|
+
the page
|
|
128
|
+
"""
|
|
129
|
+
|
|
130
|
+
Scenario: What else the path answers
|
|
131
|
+
When the following request is received:
|
|
132
|
+
"""
|
|
133
|
+
POST /.discovery/ HTTP/1.1
|
|
134
|
+
host: nex.toa.io
|
|
135
|
+
"""
|
|
136
|
+
Then the following reply is sent:
|
|
137
|
+
"""
|
|
138
|
+
405 Method Not Allowed
|
|
139
|
+
allow: GET, HEAD, OPTIONS
|
|
140
|
+
"""
|
|
141
|
+
|
|
142
|
+
Scenario: A browser at the trunk is sent to the page
|
|
143
|
+
An application serves what it declares, and `/` is usually not one of those. Someone
|
|
144
|
+
who typed the address into a browser is looking for something to look at, and the page
|
|
145
|
+
is the only thing here that is one.
|
|
146
|
+
|
|
147
|
+
When the following request is received:
|
|
148
|
+
"""
|
|
149
|
+
GET / HTTP/1.1
|
|
150
|
+
host: nex.toa.io
|
|
151
|
+
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
|
152
|
+
"""
|
|
153
|
+
Then the following reply is sent:
|
|
154
|
+
"""
|
|
155
|
+
302 Moved Temporarily
|
|
156
|
+
location: /.discovery/
|
|
157
|
+
"""
|
|
158
|
+
|
|
159
|
+
Scenario: A client that takes anything is not
|
|
160
|
+
`accept` has to prefer a page over what the gateway answers with, which is what a
|
|
161
|
+
browser sends and an API client does not.
|
|
162
|
+
|
|
163
|
+
When the following request is received:
|
|
164
|
+
"""
|
|
165
|
+
GET / HTTP/1.1
|
|
166
|
+
host: nex.toa.io
|
|
167
|
+
accept: */*
|
|
168
|
+
"""
|
|
169
|
+
Then the following reply is sent:
|
|
170
|
+
"""
|
|
171
|
+
405 Method Not Allowed
|
|
172
|
+
"""
|
|
173
|
+
|
|
174
|
+
Scenario: What the application serves there answers instead
|
|
175
|
+
A route declared at the trunk is what answers it, browser or not.
|
|
176
|
+
|
|
177
|
+
Given the annotation:
|
|
178
|
+
"""yaml
|
|
179
|
+
/:
|
|
180
|
+
anonymous: true
|
|
181
|
+
GET:
|
|
182
|
+
dev:stub: hello
|
|
183
|
+
"""
|
|
184
|
+
When the following request is received:
|
|
185
|
+
"""
|
|
186
|
+
GET / HTTP/1.1
|
|
187
|
+
host: nex.toa.io
|
|
188
|
+
accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
|
|
189
|
+
"""
|
|
190
|
+
Then the following reply is sent:
|
|
191
|
+
"""
|
|
192
|
+
200 OK
|
|
193
|
+
"""
|
|
194
|
+
And the reply does not contain:
|
|
195
|
+
"""
|
|
196
|
+
/.discovery/
|
|
197
|
+
"""
|
|
@@ -0,0 +1,466 @@
|
|
|
1
|
+
Feature: Help
|
|
2
|
+
|
|
3
|
+
What a resource and its methods are, in the words an application chooses for them. One
|
|
4
|
+
declaration, read by everything that describes a resource: `OPTIONS`, discovery, and the
|
|
5
|
+
tools MCP publishes.
|
|
6
|
+
|
|
7
|
+
Scenario: What a method is
|
|
8
|
+
A bare value is the title, which is the short thing. A sentence is written beside it.
|
|
9
|
+
|
|
10
|
+
Given the `pots` is running with the following manifest:
|
|
11
|
+
"""yaml
|
|
12
|
+
exposition:
|
|
13
|
+
/:
|
|
14
|
+
io:output: [id]
|
|
15
|
+
GET:
|
|
16
|
+
help:method: Every pot
|
|
17
|
+
endpoint: enumerate
|
|
18
|
+
POST:
|
|
19
|
+
help:method:
|
|
20
|
+
title: Put a pot on
|
|
21
|
+
description: Start a new pot brewing.
|
|
22
|
+
endpoint: create
|
|
23
|
+
"""
|
|
24
|
+
When the following request is received:
|
|
25
|
+
"""
|
|
26
|
+
OPTIONS /pots/ HTTP/1.1
|
|
27
|
+
host: nex.toa.io
|
|
28
|
+
accept: application/yaml
|
|
29
|
+
"""
|
|
30
|
+
Then the following reply is sent:
|
|
31
|
+
"""
|
|
32
|
+
200 OK
|
|
33
|
+
Allow: GET, POST
|
|
34
|
+
|
|
35
|
+
GET:
|
|
36
|
+
title: Every pot
|
|
37
|
+
POST:
|
|
38
|
+
title: Put a pot on
|
|
39
|
+
description: Start a new pot brewing.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
Scenario: What a resource is
|
|
43
|
+
A resource is described beside the methods it serves, and a verb is upper case — so
|
|
44
|
+
neither key can be mistaken for the other.
|
|
45
|
+
|
|
46
|
+
Given the `pots` is running with the following manifest:
|
|
47
|
+
"""yaml
|
|
48
|
+
exposition:
|
|
49
|
+
/:
|
|
50
|
+
help:node: Pots
|
|
51
|
+
io:output: [id]
|
|
52
|
+
GET: enumerate
|
|
53
|
+
"""
|
|
54
|
+
When the following request is received:
|
|
55
|
+
"""
|
|
56
|
+
OPTIONS /pots/ HTTP/1.1
|
|
57
|
+
host: nex.toa.io
|
|
58
|
+
accept: application/yaml
|
|
59
|
+
"""
|
|
60
|
+
Then the following reply is sent:
|
|
61
|
+
"""
|
|
62
|
+
200 OK
|
|
63
|
+
Allow: GET
|
|
64
|
+
|
|
65
|
+
title: Pots
|
|
66
|
+
GET:
|
|
67
|
+
"""
|
|
68
|
+
|
|
69
|
+
Scenario: A resource says what a method does, in the same words
|
|
70
|
+
Both take the same value: a bare one is the title, a sentence is written beside it.
|
|
71
|
+
|
|
72
|
+
Given the `pots` is running with the following manifest:
|
|
73
|
+
"""yaml
|
|
74
|
+
exposition:
|
|
75
|
+
/:
|
|
76
|
+
help:node:
|
|
77
|
+
title: Pots
|
|
78
|
+
description: What is brewing, and what is in it.
|
|
79
|
+
io:output: [id]
|
|
80
|
+
GET: enumerate
|
|
81
|
+
"""
|
|
82
|
+
When the following request is received:
|
|
83
|
+
"""
|
|
84
|
+
OPTIONS /pots/ HTTP/1.1
|
|
85
|
+
host: nex.toa.io
|
|
86
|
+
accept: application/yaml
|
|
87
|
+
"""
|
|
88
|
+
Then the following reply is sent:
|
|
89
|
+
"""
|
|
90
|
+
200 OK
|
|
91
|
+
Allow: GET
|
|
92
|
+
|
|
93
|
+
title: Pots
|
|
94
|
+
description: What is brewing, and what is in it.
|
|
95
|
+
GET:
|
|
96
|
+
"""
|
|
97
|
+
|
|
98
|
+
Scenario: What a resource is, is not said of what is under it
|
|
99
|
+
Every other directive is inherited. This one is not: a description carried downward
|
|
100
|
+
would say of every resource below that it is the one it was written for.
|
|
101
|
+
|
|
102
|
+
Given the `pots` is running with the following manifest:
|
|
103
|
+
"""yaml
|
|
104
|
+
exposition:
|
|
105
|
+
/:
|
|
106
|
+
help:node: Pots
|
|
107
|
+
io:output: [id]
|
|
108
|
+
GET: enumerate
|
|
109
|
+
/:id:
|
|
110
|
+
GET: observe
|
|
111
|
+
"""
|
|
112
|
+
When the following request is received:
|
|
113
|
+
"""
|
|
114
|
+
OPTIONS /pots/:id/ HTTP/1.1
|
|
115
|
+
host: nex.toa.io
|
|
116
|
+
accept: application/yaml
|
|
117
|
+
"""
|
|
118
|
+
Then the following reply is sent:
|
|
119
|
+
"""
|
|
120
|
+
200 OK
|
|
121
|
+
Allow: GET
|
|
122
|
+
"""
|
|
123
|
+
And the reply does not contain:
|
|
124
|
+
"""
|
|
125
|
+
Pots
|
|
126
|
+
"""
|
|
127
|
+
|
|
128
|
+
Scenario: A resource that answers in another's place is described by it
|
|
129
|
+
An intermediate node is never what a path matches — its `/` answers there instead — so
|
|
130
|
+
the two are one resource, and what describes it carries.
|
|
131
|
+
|
|
132
|
+
Given the `pots` is running with the following manifest:
|
|
133
|
+
"""yaml
|
|
134
|
+
exposition:
|
|
135
|
+
/:
|
|
136
|
+
help:node: Pots
|
|
137
|
+
/:
|
|
138
|
+
io:output: [id]
|
|
139
|
+
GET: enumerate
|
|
140
|
+
"""
|
|
141
|
+
When the following request is received:
|
|
142
|
+
"""
|
|
143
|
+
OPTIONS /pots/ HTTP/1.1
|
|
144
|
+
host: nex.toa.io
|
|
145
|
+
accept: application/yaml
|
|
146
|
+
"""
|
|
147
|
+
Then the following reply is sent:
|
|
148
|
+
"""
|
|
149
|
+
200 OK
|
|
150
|
+
Allow: GET
|
|
151
|
+
|
|
152
|
+
title: Pots
|
|
153
|
+
"""
|
|
154
|
+
|
|
155
|
+
Scenario: The whole tree carries it
|
|
156
|
+
Given the `pots` is running with the following manifest:
|
|
157
|
+
"""yaml
|
|
158
|
+
exposition:
|
|
159
|
+
/:
|
|
160
|
+
help:node: Pots
|
|
161
|
+
io:output: [id]
|
|
162
|
+
GET:
|
|
163
|
+
help:method: Every pot
|
|
164
|
+
endpoint: enumerate
|
|
165
|
+
"""
|
|
166
|
+
When the following request is received:
|
|
167
|
+
"""
|
|
168
|
+
OPTIONS /.discovery HTTP/1.1
|
|
169
|
+
host: nex.toa.io
|
|
170
|
+
accept: application/yaml
|
|
171
|
+
"""
|
|
172
|
+
Then the following reply is sent:
|
|
173
|
+
"""
|
|
174
|
+
200 OK
|
|
175
|
+
|
|
176
|
+
routes:
|
|
177
|
+
/pots:
|
|
178
|
+
title: Pots
|
|
179
|
+
GET:
|
|
180
|
+
title: Every pot
|
|
181
|
+
"""
|
|
182
|
+
|
|
183
|
+
Scenario: What the parameters are
|
|
184
|
+
A schema says `title` and `description` of what it describes, which is where these go.
|
|
185
|
+
|
|
186
|
+
Given the `echo` is running with the following manifest:
|
|
187
|
+
"""yaml
|
|
188
|
+
exposition:
|
|
189
|
+
/:first:
|
|
190
|
+
io:output: true
|
|
191
|
+
PATCH:
|
|
192
|
+
query:
|
|
193
|
+
parameters: [a]
|
|
194
|
+
help:route:
|
|
195
|
+
first: Which one
|
|
196
|
+
help:query:
|
|
197
|
+
a:
|
|
198
|
+
title: The one to greet
|
|
199
|
+
description: A name, or nothing to greet the world.
|
|
200
|
+
endpoint: parameters
|
|
201
|
+
"""
|
|
202
|
+
When the following request is received:
|
|
203
|
+
"""
|
|
204
|
+
OPTIONS /echo/:first/ HTTP/1.1
|
|
205
|
+
host: nex.toa.io
|
|
206
|
+
accept: application/yaml
|
|
207
|
+
"""
|
|
208
|
+
Then the following reply is sent:
|
|
209
|
+
"""
|
|
210
|
+
200 OK
|
|
211
|
+
Allow: PATCH
|
|
212
|
+
|
|
213
|
+
PATCH:
|
|
214
|
+
route:
|
|
215
|
+
first:
|
|
216
|
+
title: Which one
|
|
217
|
+
query:
|
|
218
|
+
a:
|
|
219
|
+
type: string
|
|
220
|
+
title: The one to greet
|
|
221
|
+
description: A name, or nothing to greet the world.
|
|
222
|
+
"""
|
|
223
|
+
|
|
224
|
+
Scenario: A segment a mapping renamed
|
|
225
|
+
`map:segments` answers a variable under the property it fills, so that is the name it
|
|
226
|
+
is described by — the template's is nowhere in the answer.
|
|
227
|
+
|
|
228
|
+
Given the `echo` is running with the following manifest:
|
|
229
|
+
"""yaml
|
|
230
|
+
exposition:
|
|
231
|
+
/:first:
|
|
232
|
+
io:output: true
|
|
233
|
+
PATCH:
|
|
234
|
+
map:segments:
|
|
235
|
+
a: first
|
|
236
|
+
help:route:
|
|
237
|
+
a: Which one
|
|
238
|
+
endpoint: parameters
|
|
239
|
+
"""
|
|
240
|
+
When the following request is received:
|
|
241
|
+
"""
|
|
242
|
+
OPTIONS /echo/:first/ HTTP/1.1
|
|
243
|
+
host: nex.toa.io
|
|
244
|
+
accept: application/yaml
|
|
245
|
+
"""
|
|
246
|
+
Then the following reply is sent:
|
|
247
|
+
"""
|
|
248
|
+
200 OK
|
|
249
|
+
Allow: PATCH
|
|
250
|
+
|
|
251
|
+
PATCH:
|
|
252
|
+
route:
|
|
253
|
+
a:
|
|
254
|
+
type: string
|
|
255
|
+
title: Which one
|
|
256
|
+
"""
|
|
257
|
+
And the reply does not contain:
|
|
258
|
+
"""
|
|
259
|
+
first:
|
|
260
|
+
"""
|
|
261
|
+
|
|
262
|
+
Scenario: A parameter nothing answers
|
|
263
|
+
A name that is nobody's is not answered. The template is not what says them all, so
|
|
264
|
+
there is nothing to check it against where it is written.
|
|
265
|
+
|
|
266
|
+
Given the `pots` is running with the following manifest:
|
|
267
|
+
"""yaml
|
|
268
|
+
exposition:
|
|
269
|
+
/:id:
|
|
270
|
+
io:output: [id]
|
|
271
|
+
GET:
|
|
272
|
+
help:route:
|
|
273
|
+
pot: Which pot
|
|
274
|
+
help:query:
|
|
275
|
+
nothing: Nobody
|
|
276
|
+
endpoint: observe
|
|
277
|
+
"""
|
|
278
|
+
When the following request is received:
|
|
279
|
+
"""
|
|
280
|
+
OPTIONS /pots/:id/ HTTP/1.1
|
|
281
|
+
host: nex.toa.io
|
|
282
|
+
accept: application/yaml
|
|
283
|
+
"""
|
|
284
|
+
Then the following reply is sent:
|
|
285
|
+
"""
|
|
286
|
+
200 OK
|
|
287
|
+
Allow: GET
|
|
288
|
+
"""
|
|
289
|
+
And the reply does not contain:
|
|
290
|
+
"""
|
|
291
|
+
Which pot
|
|
292
|
+
"""
|
|
293
|
+
|
|
294
|
+
Scenario: What guards a resource
|
|
295
|
+
`auth` says what reaching one takes, so that a reader is not offered what is not theirs
|
|
296
|
+
and can tell the machinery from what the application serves.
|
|
297
|
+
|
|
298
|
+
Given the annotation:
|
|
299
|
+
"""yaml
|
|
300
|
+
/:
|
|
301
|
+
anonymous: true
|
|
302
|
+
/pots:
|
|
303
|
+
GET:
|
|
304
|
+
auth:anyone: true
|
|
305
|
+
dev:stub: []
|
|
306
|
+
/accounts/:id:
|
|
307
|
+
GET:
|
|
308
|
+
auth:id: id
|
|
309
|
+
dev:stub: []
|
|
310
|
+
/admin:
|
|
311
|
+
GET:
|
|
312
|
+
auth:role: operator
|
|
313
|
+
dev:stub: []
|
|
314
|
+
/introspection:
|
|
315
|
+
GET:
|
|
316
|
+
auth:role: system:introspection
|
|
317
|
+
dev:stub: []
|
|
318
|
+
"""
|
|
319
|
+
When the following request is received:
|
|
320
|
+
"""
|
|
321
|
+
OPTIONS /.discovery HTTP/1.1
|
|
322
|
+
host: nex.toa.io
|
|
323
|
+
accept: application/yaml
|
|
324
|
+
"""
|
|
325
|
+
Then the following reply is sent:
|
|
326
|
+
"""
|
|
327
|
+
200 OK
|
|
328
|
+
|
|
329
|
+
routes:
|
|
330
|
+
/accounts/:id:
|
|
331
|
+
private: true
|
|
332
|
+
GET:
|
|
333
|
+
private: true
|
|
334
|
+
/admin:
|
|
335
|
+
protected: true
|
|
336
|
+
GET:
|
|
337
|
+
protected: true
|
|
338
|
+
/introspection:
|
|
339
|
+
protected: true
|
|
340
|
+
system: true
|
|
341
|
+
GET:
|
|
342
|
+
protected: true
|
|
343
|
+
system: true
|
|
344
|
+
/pots:
|
|
345
|
+
authenticated: true
|
|
346
|
+
GET:
|
|
347
|
+
authenticated: true
|
|
348
|
+
"""
|
|
349
|
+
|
|
350
|
+
Scenario: A rule says what each of its own says
|
|
351
|
+
`auth:rule` is the directives it composes, and describing one is describing them.
|
|
352
|
+
|
|
353
|
+
Given the annotation:
|
|
354
|
+
"""yaml
|
|
355
|
+
/:
|
|
356
|
+
anonymous: true
|
|
357
|
+
/papers/:id:
|
|
358
|
+
GET:
|
|
359
|
+
auth:rule:
|
|
360
|
+
id: id
|
|
361
|
+
role: system:archive
|
|
362
|
+
dev:stub: []
|
|
363
|
+
"""
|
|
364
|
+
When the following request is received:
|
|
365
|
+
"""
|
|
366
|
+
OPTIONS /papers/:id/ HTTP/1.1
|
|
367
|
+
host: nex.toa.io
|
|
368
|
+
accept: application/yaml
|
|
369
|
+
"""
|
|
370
|
+
Then the following reply is sent:
|
|
371
|
+
"""
|
|
372
|
+
200 OK
|
|
373
|
+
Allow: GET
|
|
374
|
+
|
|
375
|
+
private: true
|
|
376
|
+
protected: true
|
|
377
|
+
system: true
|
|
378
|
+
GET:
|
|
379
|
+
private: true
|
|
380
|
+
protected: true
|
|
381
|
+
system: true
|
|
382
|
+
"""
|
|
383
|
+
|
|
384
|
+
Scenario: A tool is called what the method is called
|
|
385
|
+
`mcp:tool` says only whether the method is published; what it is, is `help:method`.
|
|
386
|
+
|
|
387
|
+
Given the annotation:
|
|
388
|
+
"""yaml
|
|
389
|
+
mcp:
|
|
390
|
+
name: Pots
|
|
391
|
+
anonymous: true
|
|
392
|
+
/:
|
|
393
|
+
anonymous: true
|
|
394
|
+
/pots:
|
|
395
|
+
GET:
|
|
396
|
+
mcp:tool: true
|
|
397
|
+
help:method:
|
|
398
|
+
title: Hot pots
|
|
399
|
+
description: The pots that are too hot to pour.
|
|
400
|
+
dev:stub: []
|
|
401
|
+
"""
|
|
402
|
+
When the following request is received:
|
|
403
|
+
"""
|
|
404
|
+
POST /.mcp HTTP/1.1
|
|
405
|
+
host: nex.toa.io
|
|
406
|
+
content-type: application/json
|
|
407
|
+
accept: application/yaml
|
|
408
|
+
mcp-protocol-version: 2026-07-28
|
|
409
|
+
mcp-method: tools/list
|
|
410
|
+
|
|
411
|
+
{"jsonrpc": "2.0", "id": 1, "method": "tools/list",
|
|
412
|
+
"params": {"_meta": {"io.modelcontextprotocol/protocolVersion": "2026-07-28",
|
|
413
|
+
"io.modelcontextprotocol/clientCapabilities": {}}}}
|
|
414
|
+
"""
|
|
415
|
+
Then the following reply is sent:
|
|
416
|
+
"""
|
|
417
|
+
200 OK
|
|
418
|
+
|
|
419
|
+
- name: pots.GET
|
|
420
|
+
title: Hot pots
|
|
421
|
+
description: The pots that are too hot to pour.
|
|
422
|
+
"""
|
|
423
|
+
# and the resource says of itself that it publishes one
|
|
424
|
+
When the following request is received:
|
|
425
|
+
"""
|
|
426
|
+
OPTIONS /pots/ HTTP/1.1
|
|
427
|
+
host: nex.toa.io
|
|
428
|
+
accept: application/yaml
|
|
429
|
+
"""
|
|
430
|
+
Then the following reply is sent:
|
|
431
|
+
"""
|
|
432
|
+
200 OK
|
|
433
|
+
Allow: GET
|
|
434
|
+
|
|
435
|
+
GET:
|
|
436
|
+
title: Hot pots
|
|
437
|
+
description: The pots that are too hot to pour.
|
|
438
|
+
mcp: true
|
|
439
|
+
"""
|
|
440
|
+
|
|
441
|
+
Scenario: A resource that serves nothing cannot be described
|
|
442
|
+
Nothing would carry what it says, so the declaration is refused where it is written
|
|
443
|
+
rather than found missing from the answer.
|
|
444
|
+
|
|
445
|
+
Given the `pots` is running with the following manifest:
|
|
446
|
+
"""yaml
|
|
447
|
+
exposition:
|
|
448
|
+
/:
|
|
449
|
+
io:output: [id]
|
|
450
|
+
GET: enumerate
|
|
451
|
+
/brewing:
|
|
452
|
+
help:node: Brewing
|
|
453
|
+
/:id:
|
|
454
|
+
GET: observe
|
|
455
|
+
"""
|
|
456
|
+
# a route only this declaration serves: a refused branch leaves the one before it
|
|
457
|
+
When the following request is received:
|
|
458
|
+
"""
|
|
459
|
+
OPTIONS /pots/brewing/:id/ HTTP/1.1
|
|
460
|
+
host: nex.toa.io
|
|
461
|
+
accept: application/yaml
|
|
462
|
+
"""
|
|
463
|
+
Then the following reply is sent:
|
|
464
|
+
"""
|
|
465
|
+
404 Not Found
|
|
466
|
+
"""
|