transn-yapi-mcp 0.1.1 → 0.1.3
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/README.md +58 -67
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/dist/tools.js +45 -8
- package/dist/tools.js.map +1 -1
- package/package.json +2 -2
- package/src/index.ts +5 -1
- package/src/tools.ts +48 -9
package/README.md
CHANGED
|
@@ -1,29 +1,21 @@
|
|
|
1
1
|
# transn-yapi-mcp
|
|
2
2
|
|
|
3
|
-
|
|
4
|
-
its API definitions as tools for AI agents or MCP-compatible clients.
|
|
3
|
+
一个 MCP (Model Context Protocol) 服务器,用于从 YAPI 获取接口定义,并通过 MCP 工具暴露给 AI(如 Cursor、Claude),便于自动生成代码和文档。
|
|
5
4
|
|
|
6
|
-
##
|
|
5
|
+
## 功能概览
|
|
7
6
|
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
- name
|
|
12
|
-
- request
|
|
13
|
-
-
|
|
7
|
+
- 根据 **interfaceId** 查询单个接口定义
|
|
8
|
+
- 根据 **path + projectId** 查询指定项目下的接口定义
|
|
9
|
+
- 返回单一 JSON 对象形式的接口元数据(通过 MCP `CallToolResult.content[0].text` 返回),包含:
|
|
10
|
+
- 基础信息:`name`、`path`、`method`、`description`
|
|
11
|
+
- 请求参数定义:`request.query`、`request.bodySchema`(已解析的 JSON Schema 对象)
|
|
12
|
+
- 响应结构定义:`responseSchema`(已解析的 JSON Schema 对象)
|
|
14
13
|
|
|
15
|
-
##
|
|
14
|
+
## 安装与配置
|
|
16
15
|
|
|
17
|
-
|
|
18
|
-
npm install transn-yapi-mcp
|
|
19
|
-
# or
|
|
20
|
-
pnpm add transn-yapi-mcp
|
|
21
|
-
```
|
|
22
|
-
|
|
23
|
-
## Usage
|
|
16
|
+
### 使用 npx 启动(推荐,无需预装)
|
|
24
17
|
|
|
25
|
-
|
|
26
|
-
Typical MCP client configuration (example):
|
|
18
|
+
在 MCP 客户端(例如 Cursor)的配置文件中添加:
|
|
27
19
|
|
|
28
20
|
```jsonc
|
|
29
21
|
{
|
|
@@ -31,7 +23,11 @@ Typical MCP client configuration (example):
|
|
|
31
23
|
"yapi-mcp": {
|
|
32
24
|
"type": "stdio",
|
|
33
25
|
"command": "npx",
|
|
34
|
-
"args": [
|
|
26
|
+
"args": [
|
|
27
|
+
"--yes",
|
|
28
|
+
"--package=transn-yapi-mcp@0.1.3",
|
|
29
|
+
"transn-yapi-mcp"
|
|
30
|
+
],
|
|
35
31
|
"env": {
|
|
36
32
|
"YAPI_BASE": "https://your-yapi-domain.com",
|
|
37
33
|
"YAPI_TOKEN": "your_yapi_openapi_token"
|
|
@@ -41,66 +37,61 @@ Typical MCP client configuration (example):
|
|
|
41
37
|
}
|
|
42
38
|
```
|
|
43
39
|
|
|
44
|
-
|
|
40
|
+
> 说明:
|
|
41
|
+
> - 无需在本地单独安装 `transn-yapi-mcp`,npx 会在首次启动时自动下载对应版本
|
|
42
|
+
> - 之后会从本地缓存加载,提高启动速度
|
|
45
43
|
|
|
46
|
-
|
|
47
|
-
Base URL of your YAPI instance, without a trailing slash.
|
|
48
|
-
Example: `https://yapi.example.com`
|
|
44
|
+
### 环境变量说明
|
|
49
45
|
|
|
50
|
-
- `
|
|
51
|
-
|
|
46
|
+
- `YAPI_BASE`:YAPI 实例基础地址(不带尾部斜杠),如 `https://yapi.example.com`
|
|
47
|
+
- `YAPI_TOKEN`:对应项目的 openapi token,将作为 `token` 查询参数传递
|
|
52
48
|
|
|
53
|
-
##
|
|
49
|
+
## 暴露的工具
|
|
54
50
|
|
|
55
|
-
### `
|
|
51
|
+
### `yapi_get_api_context`
|
|
56
52
|
|
|
57
|
-
|
|
53
|
+
- 功能:通过接口 ID 获取接口定义
|
|
54
|
+
- 输入:
|
|
55
|
+
- `interfaceId: number`(必填)
|
|
56
|
+
- `projectId?: number`(可选)
|
|
57
|
+
- 输出:
|
|
58
|
+
- JSON 对象,字段示例:
|
|
59
|
+
- `name` / `path` / `method` / `description`
|
|
60
|
+
- `request.query`:查询参数定义(源自 YAPI `req_query`)
|
|
61
|
+
- `request.bodySchema`:请求体 JSON Schema(解析自 YAPI `req_body_other`)
|
|
62
|
+
- `responseSchema`:响应 JSON Schema(解析自 YAPI `res_body`)
|
|
58
63
|
|
|
59
|
-
|
|
64
|
+
### `yapi_get_api_context_by_path`
|
|
60
65
|
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
**Output (shape, simplified)**
|
|
69
|
-
|
|
70
|
-
```ts
|
|
71
|
-
{
|
|
72
|
-
name: string;
|
|
73
|
-
path: string;
|
|
74
|
-
method: string;
|
|
75
|
-
description: string;
|
|
76
|
-
request: {
|
|
77
|
-
query: unknown;
|
|
78
|
-
body: unknown;
|
|
79
|
-
};
|
|
80
|
-
response: unknown;
|
|
81
|
-
}
|
|
82
|
-
```
|
|
66
|
+
- 功能:通过 `projectId + path (+ method)` 获取接口定义
|
|
67
|
+
- 输入:
|
|
68
|
+
- `projectId: number`
|
|
69
|
+
- `path: string`
|
|
70
|
+
- `method?: string`
|
|
71
|
+
- 输出:
|
|
72
|
+
- 同 `yapi_get_api_context`,为同一结构的 JSON 对象
|
|
83
73
|
|
|
84
|
-
|
|
74
|
+
> 在 MCP 协议层面,这两个工具都会返回:
|
|
75
|
+
> - 一个 `CallToolResult` 对象,其中 `content[0].type = "text"`
|
|
76
|
+
> - `content[0].text` 为上述 JSON 对象的字符串表示,便于 AI 解析使用
|
|
85
77
|
|
|
86
|
-
|
|
78
|
+
## 开发简单说明
|
|
87
79
|
|
|
88
|
-
|
|
80
|
+
- Node.js >= 18
|
|
81
|
+
- 推荐使用 pnpm:
|
|
89
82
|
|
|
90
|
-
```
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
path: string; // required, e.g. "/busi/patient/detail_by_openid"
|
|
94
|
-
method?: string; // optional, e.g. "GET"
|
|
95
|
-
}
|
|
83
|
+
```bash
|
|
84
|
+
pnpm install
|
|
85
|
+
pnpm build
|
|
96
86
|
```
|
|
97
87
|
|
|
98
|
-
|
|
88
|
+
本地直接运行 MCP 服务器用于调试:
|
|
99
89
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
MIT
|
|
90
|
+
```bash
|
|
91
|
+
YAPI_BASE=https://your-yapi-domain.com YAPI_TOKEN=your_token node dist/index.js
|
|
92
|
+
```
|
|
105
93
|
|
|
94
|
+
## 许可证与作者
|
|
106
95
|
|
|
96
|
+
- 许可证:MIT
|
|
97
|
+
- 作者:merrick <1973231806@qq.com>
|
package/dist/index.js
CHANGED
|
@@ -4,7 +4,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
4
4
|
import { tools } from "./tools.js";
|
|
5
5
|
const server = new McpServer({
|
|
6
6
|
name: "transn-yapi-mcp",
|
|
7
|
-
version: "0.1.
|
|
7
|
+
version: "0.1.3"
|
|
8
8
|
});
|
|
9
9
|
// 注册工具
|
|
10
10
|
for (const tool of tools) {
|
|
@@ -15,4 +15,8 @@ for (const tool of tools) {
|
|
|
15
15
|
}
|
|
16
16
|
const transport = new StdioServerTransport();
|
|
17
17
|
await server.connect(transport);
|
|
18
|
+
// 确保在无输入时进程不立即退出,便于被 MCP 客户端保持长连接
|
|
19
|
+
process.stdin.resume();
|
|
20
|
+
// 兜底保活,避免在某些环境下事件循环清空直接退出
|
|
21
|
+
setInterval(() => { }, 1 << 30);
|
|
18
22
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,OAAO;AACP,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;IACzB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAEnC,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,iBAAiB;IACvB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,OAAO;AACP,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;IACzB,MAAM,CAAC,YAAY,CAAC,IAAI,CAAC,IAAI,EAAE;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,WAAW,EAAE,IAAI,CAAC,WAAW;KAC9B,EAAE,IAAI,CAAC,OAAO,CAAC,CAAC;AACnB,CAAC;AAED,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAChC,kCAAkC;AAClC,OAAO,CAAC,KAAK,CAAC,MAAM,EAAE,CAAC;AACvB,0BAA0B;AAC1B,WAAW,CAAC,GAAG,EAAE,GAAE,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,CAAC"}
|
package/dist/tools.js
CHANGED
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
// src/tools.ts
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { getInterface, getInterfaceByPath } from "./yapi.js";
|
|
4
|
+
/**
|
|
5
|
+
* 安全解析 JSON 字符串:
|
|
6
|
+
* - 如果是合法 JSON 字符串,则返回解析后的对象;
|
|
7
|
+
* - 否则直接返回原始值(避免因单个接口配置问题导致工具整体不可用)。
|
|
8
|
+
*/
|
|
9
|
+
function parseJsonSafe(value) {
|
|
10
|
+
if (typeof value !== "string") {
|
|
11
|
+
return value;
|
|
12
|
+
}
|
|
13
|
+
try {
|
|
14
|
+
return JSON.parse(value);
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
3
20
|
export const tools = [
|
|
4
21
|
{
|
|
5
|
-
|
|
22
|
+
// 工具名用简单下划线,避免客户端再做转换
|
|
23
|
+
name: "yapi_get_api_context",
|
|
6
24
|
description: "Get structured API context from YAPI",
|
|
7
25
|
inputSchema: z.object({
|
|
8
26
|
projectId: z.number().optional(),
|
|
@@ -12,21 +30,32 @@ export const tools = [
|
|
|
12
30
|
const api = projectId
|
|
13
31
|
? await getInterface(projectId, interfaceId)
|
|
14
32
|
: await getInterface(interfaceId);
|
|
15
|
-
|
|
33
|
+
const payload = {
|
|
16
34
|
name: api.title,
|
|
17
35
|
path: api.path,
|
|
18
36
|
method: api.method,
|
|
19
37
|
description: api.desc,
|
|
20
38
|
request: {
|
|
21
39
|
query: api.req_query,
|
|
22
|
-
|
|
40
|
+
// 将 YAPI 返回的 JSON Schema 字符串解析为对象,便于 AI 工具直接理解字段结构
|
|
41
|
+
bodySchema: parseJsonSafe(api.req_body_other)
|
|
23
42
|
},
|
|
24
|
-
|
|
43
|
+
// 同样对响应结构做解析
|
|
44
|
+
responseSchema: parseJsonSafe(api.res_body)
|
|
45
|
+
};
|
|
46
|
+
// ★ 关键:返回 MCP 标准的 CallToolResult
|
|
47
|
+
return {
|
|
48
|
+
content: [
|
|
49
|
+
{
|
|
50
|
+
type: "text",
|
|
51
|
+
text: JSON.stringify(payload)
|
|
52
|
+
}
|
|
53
|
+
]
|
|
25
54
|
};
|
|
26
55
|
}
|
|
27
56
|
},
|
|
28
57
|
{
|
|
29
|
-
name: "
|
|
58
|
+
name: "yapi_get_api_context_by_path",
|
|
30
59
|
description: "Get API context by path within a YAPI project",
|
|
31
60
|
inputSchema: z.object({
|
|
32
61
|
projectId: z.number(),
|
|
@@ -35,16 +64,24 @@ export const tools = [
|
|
|
35
64
|
}),
|
|
36
65
|
handler: async ({ projectId, path, method }) => {
|
|
37
66
|
const api = await getInterfaceByPath(projectId, path, method);
|
|
38
|
-
|
|
67
|
+
const payload = {
|
|
39
68
|
name: api.title,
|
|
40
69
|
path: api.path,
|
|
41
70
|
method: api.method,
|
|
42
71
|
description: api.desc,
|
|
43
72
|
request: {
|
|
44
73
|
query: api.req_query,
|
|
45
|
-
|
|
74
|
+
bodySchema: parseJsonSafe(api.req_body_other)
|
|
46
75
|
},
|
|
47
|
-
|
|
76
|
+
responseSchema: parseJsonSafe(api.res_body)
|
|
77
|
+
};
|
|
78
|
+
return {
|
|
79
|
+
content: [
|
|
80
|
+
{
|
|
81
|
+
type: "text",
|
|
82
|
+
text: JSON.stringify(payload)
|
|
83
|
+
}
|
|
84
|
+
]
|
|
48
85
|
};
|
|
49
86
|
}
|
|
50
87
|
}
|
package/dist/tools.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AACxB,OAAO,EAAE,YAAY,EAAE,kBAAkB,EAAE,MAAM,WAAW,CAAC;AAE7D,MAAM,CAAC,MAAM,KAAK,GAAG;IACnB;QACE,IAAI,EAAE,sBAAsB;QAC5B,WAAW,EAAE,sCAAsC;QACnD,WAAW,EAAE,CAAC,CAAC,MAAM,CAAC;YACpB,SAAS,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;YAChC,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE;SACxB,CAAC;QACF,OAAO,EAAE,KAAK,EAAE,EAAE,SAAS,EAAE,WAAW,EAA+C,EAAE,EAAE;YACzF,MAAM,GAAG,GAAG,SAAS;gBACnB,CAAC,CAAC,MAAM,YAAY,CAAC,SAAS,EAAE,WAAW,CAAC;gBAC5C,CAAC,CAAC,MAAM,YAAY,CAAC,WAAW,CAAC,CAAC;YAEpC,OAAO;
|
|
1
|
+
{"version":3,"file":"tools.js","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "transn-yapi-mcp",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.3",
|
|
4
4
|
"description": "An MCP server that fetches structured API metadata from YAPI and exposes it as tools.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
},
|
|
22
22
|
"author": {
|
|
23
23
|
"name": "merrick",
|
|
24
|
-
"email": "
|
|
24
|
+
"email": "1973231806@qq.com"
|
|
25
25
|
},
|
|
26
26
|
"dependencies": {
|
|
27
27
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
package/src/index.ts
CHANGED
|
@@ -5,7 +5,7 @@ import { tools } from "./tools.js";
|
|
|
5
5
|
|
|
6
6
|
const server = new McpServer({
|
|
7
7
|
name: "transn-yapi-mcp",
|
|
8
|
-
version: "0.1.
|
|
8
|
+
version: "0.1.3"
|
|
9
9
|
});
|
|
10
10
|
|
|
11
11
|
// 注册工具
|
|
@@ -18,3 +18,7 @@ for (const tool of tools) {
|
|
|
18
18
|
|
|
19
19
|
const transport = new StdioServerTransport();
|
|
20
20
|
await server.connect(transport);
|
|
21
|
+
// 确保在无输入时进程不立即退出,便于被 MCP 客户端保持长连接
|
|
22
|
+
process.stdin.resume();
|
|
23
|
+
// 兜底保活,避免在某些环境下事件循环清空直接退出
|
|
24
|
+
setInterval(() => {}, 1 << 30);
|
package/src/tools.ts
CHANGED
|
@@ -1,9 +1,27 @@
|
|
|
1
|
+
// src/tools.ts
|
|
1
2
|
import { z } from "zod";
|
|
2
3
|
import { getInterface, getInterfaceByPath } from "./yapi.js";
|
|
3
4
|
|
|
5
|
+
/**
|
|
6
|
+
* 安全解析 JSON 字符串:
|
|
7
|
+
* - 如果是合法 JSON 字符串,则返回解析后的对象;
|
|
8
|
+
* - 否则直接返回原始值(避免因单个接口配置问题导致工具整体不可用)。
|
|
9
|
+
*/
|
|
10
|
+
function parseJsonSafe(value: unknown): unknown {
|
|
11
|
+
if (typeof value !== "string") {
|
|
12
|
+
return value;
|
|
13
|
+
}
|
|
14
|
+
try {
|
|
15
|
+
return JSON.parse(value);
|
|
16
|
+
} catch {
|
|
17
|
+
return value;
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
|
|
4
21
|
export const tools = [
|
|
5
22
|
{
|
|
6
|
-
|
|
23
|
+
// 工具名用简单下划线,避免客户端再做转换
|
|
24
|
+
name: "yapi_get_api_context",
|
|
7
25
|
description: "Get structured API context from YAPI",
|
|
8
26
|
inputSchema: z.object({
|
|
9
27
|
projectId: z.number().optional(),
|
|
@@ -14,21 +32,33 @@ export const tools = [
|
|
|
14
32
|
? await getInterface(projectId, interfaceId)
|
|
15
33
|
: await getInterface(interfaceId);
|
|
16
34
|
|
|
17
|
-
|
|
35
|
+
const payload = {
|
|
18
36
|
name: api.title,
|
|
19
37
|
path: api.path,
|
|
20
38
|
method: api.method,
|
|
21
39
|
description: api.desc,
|
|
22
40
|
request: {
|
|
23
41
|
query: api.req_query,
|
|
24
|
-
|
|
42
|
+
// 将 YAPI 返回的 JSON Schema 字符串解析为对象,便于 AI 工具直接理解字段结构
|
|
43
|
+
bodySchema: parseJsonSafe(api.req_body_other)
|
|
25
44
|
},
|
|
26
|
-
|
|
45
|
+
// 同样对响应结构做解析
|
|
46
|
+
responseSchema: parseJsonSafe(api.res_body)
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
// ★ 关键:返回 MCP 标准的 CallToolResult
|
|
50
|
+
return {
|
|
51
|
+
content: [
|
|
52
|
+
{
|
|
53
|
+
type: "text",
|
|
54
|
+
text: JSON.stringify(payload)
|
|
55
|
+
}
|
|
56
|
+
]
|
|
27
57
|
};
|
|
28
58
|
}
|
|
29
59
|
},
|
|
30
60
|
{
|
|
31
|
-
name: "
|
|
61
|
+
name: "yapi_get_api_context_by_path",
|
|
32
62
|
description: "Get API context by path within a YAPI project",
|
|
33
63
|
inputSchema: z.object({
|
|
34
64
|
projectId: z.number(),
|
|
@@ -40,17 +70,26 @@ export const tools = [
|
|
|
40
70
|
) => {
|
|
41
71
|
const api = await getInterfaceByPath(projectId, path, method);
|
|
42
72
|
|
|
43
|
-
|
|
73
|
+
const payload = {
|
|
44
74
|
name: api.title,
|
|
45
75
|
path: api.path,
|
|
46
76
|
method: api.method,
|
|
47
77
|
description: api.desc,
|
|
48
78
|
request: {
|
|
49
79
|
query: api.req_query,
|
|
50
|
-
|
|
80
|
+
bodySchema: parseJsonSafe(api.req_body_other)
|
|
51
81
|
},
|
|
52
|
-
|
|
82
|
+
responseSchema: parseJsonSafe(api.res_body)
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
return {
|
|
86
|
+
content: [
|
|
87
|
+
{
|
|
88
|
+
type: "text",
|
|
89
|
+
text: JSON.stringify(payload)
|
|
90
|
+
}
|
|
91
|
+
]
|
|
53
92
|
};
|
|
54
93
|
}
|
|
55
94
|
}
|
|
56
|
-
];
|
|
95
|
+
];
|