@sassoftware/sas-score-mcp-serverjs 0.0.2
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/.babelrc +6 -0
- package/.env +13 -0
- package/.env.http +29 -0
- package/CHANGES.md +2 -0
- package/CONTRIBUTING.md +14 -0
- package/ContributorAgreement.txt +56 -0
- package/LICENSE +205 -0
- package/LICENSES.json +105 -0
- package/QUICK_REFERENCE.md +378 -0
- package/README.md +267 -0
- package/SECURITY.md +31 -0
- package/SUPPORT.md +3 -0
- package/TOOL_DESCRIPTION_TEMPLATE.md +157 -0
- package/TOOL_UPDATES_SUMMARY.md +208 -0
- package/cli.js +214 -0
- package/labs/.subclass.json +13 -0
- package/labs/README.md +4 -0
- package/mcpConfigurations/README.md +3 -0
- package/mcpConfigurations/http.json +8 -0
- package/mcpConfigurations/stdio.json +20 -0
- package/mcpConfigurations/stdiodev.json +20 -0
- package/mcpserver.png +0 -0
- package/openApi.json +106 -0
- package/openApi.yaml +84 -0
- package/package.json +72 -0
- package/sas-mcp-tools-reference.md +600 -0
- package/sasCode/sas-sql-query.sas +33 -0
- package/sasCode/sas_sql_tool.json +237 -0
- package/scripts/getViyaca.sh +8 -0
- package/src/core.js +19 -0
- package/src/coreSSE.js +14 -0
- package/src/corehttp.js +335 -0
- package/src/createHttpTransport.js +26 -0
- package/src/createMcpServer.js +76 -0
- package/src/db/scrModels.js +23 -0
- package/src/toolSet/devaScore.js +69 -0
- package/src/toolSet/findJob.js +90 -0
- package/src/toolSet/findJobdef.js +95 -0
- package/src/toolSet/findLibrary.js +100 -0
- package/src/toolSet/findModel.js +83 -0
- package/src/toolSet/findTable.js +94 -0
- package/src/toolSet/getEnv.js +72 -0
- package/src/toolSet/listJobdefs.js +96 -0
- package/src/toolSet/listJobs.js +110 -0
- package/src/toolSet/listLibraries.js +90 -0
- package/src/toolSet/listModels.js +83 -0
- package/src/toolSet/listTables.js +95 -0
- package/src/toolSet/makeTools.js +75 -0
- package/src/toolSet/mcp server .png +0 -0
- package/src/toolSet/modelInfo.js +87 -0
- package/src/toolSet/modelScore.js +131 -0
- package/src/toolSet/readTable.js +104 -0
- package/src/toolSet/runCasProgram.js +118 -0
- package/src/toolSet/runJob.js +81 -0
- package/src/toolSet/runJobdef.js +85 -0
- package/src/toolSet/runMacro.js +82 -0
- package/src/toolSet/runProgram.js +145 -0
- package/src/toolSet/sasQuery.js +126 -0
- package/src/toolSet/sasQueryTemplate.js +148 -0
- package/src/toolSet/sasQueryTemplate2.js +140 -0
- package/src/toolSet/scrInfo.js +55 -0
- package/src/toolSet/scrScore.js +71 -0
- package/src/toolSet/searchAssets.js +52 -0
- package/src/toolSet/setContext.js +98 -0
- package/src/toolSet/superstat.js +60 -0
- package/src/toolSet/tableInfo.js +102 -0
- package/src/toolhelpers/_catalogSearch.js +87 -0
- package/src/toolhelpers/_getEnv.js +10 -0
- package/src/toolhelpers/_itemsData.js +28 -0
- package/src/toolhelpers/_jobSubmit.js +78 -0
- package/src/toolhelpers/_listJobdefs.js +59 -0
- package/src/toolhelpers/_listJobs.js +63 -0
- package/src/toolhelpers/_listLibrary.js +56 -0
- package/src/toolhelpers/_listModels.js +41 -0
- package/src/toolhelpers/_listTables.js +52 -0
- package/src/toolhelpers/_masDescribe.js +27 -0
- package/src/toolhelpers/_masScoring.js +64 -0
- package/src/toolhelpers/_readTable.js +69 -0
- package/src/toolhelpers/_scrInfo.js +32 -0
- package/src/toolhelpers/_scrScore.js +49 -0
- package/src/toolhelpers/_submitCasl.js +34 -0
- package/src/toolhelpers/_submitCode.js +96 -0
- package/src/toolhelpers/_submitMacro.js +24 -0
- package/src/toolhelpers/_tableColumns.js +61 -0
- package/src/toolhelpers/_tableInfo.js +72 -0
- package/src/toolhelpers/deleteSession.js +13 -0
- package/src/toolhelpers/getLogonPayload.js +100 -0
- package/src/toolhelpers/getOpts.js +43 -0
- package/src/toolhelpers/getOptsViya.js +38 -0
- package/src/toolhelpers/getStoreOpts.js +18 -0
- package/src/toolhelpers/getToken.js +40 -0
- package/src/toolhelpers/refreshToken.js +48 -0
- package/test/README.md +63 -0
- package/test/listLibraries.test.js +245 -0
- package/tool-developer-guide.md +80 -0
- package/types.js +25 -0
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
{
|
|
2
|
+
"creationTimeStamp": "2025-11-24T14:17:59.145Z",
|
|
3
|
+
"modifiedTimeStamp": "2025-11-24T14:17:59.689Z",
|
|
4
|
+
"createdBy": "deva.kumar@sas.com",
|
|
5
|
+
"modifiedBy": "deva.kumar@sas.com",
|
|
6
|
+
"links": [
|
|
7
|
+
{
|
|
8
|
+
"method": "GET",
|
|
9
|
+
"rel": "self",
|
|
10
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
11
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
12
|
+
"type": "application/vnd.sas.transfer.package"
|
|
13
|
+
},
|
|
14
|
+
{
|
|
15
|
+
"method": "GET",
|
|
16
|
+
"rel": "alternate",
|
|
17
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
18
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
19
|
+
"type": "application/vnd.sas.transfer.package.summary"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"method": "DELETE",
|
|
23
|
+
"rel": "delete",
|
|
24
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
25
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452"
|
|
26
|
+
},
|
|
27
|
+
{
|
|
28
|
+
"method": "GET",
|
|
29
|
+
"rel": "objects",
|
|
30
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects",
|
|
31
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects",
|
|
32
|
+
"type": "application/vnd.sas.collection"
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
"method": "GET",
|
|
36
|
+
"rel": "connectors",
|
|
37
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/connectors",
|
|
38
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/connectors",
|
|
39
|
+
"type": "application/vnd.sas.collection"
|
|
40
|
+
},
|
|
41
|
+
{
|
|
42
|
+
"method": "GET",
|
|
43
|
+
"rel": "mapping",
|
|
44
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/mapping",
|
|
45
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/mapping",
|
|
46
|
+
"type": "application/vnd.sas.transfer.import.mapping"
|
|
47
|
+
}
|
|
48
|
+
],
|
|
49
|
+
"version": 1,
|
|
50
|
+
"id": "ad51d696-6a90-41ac-bb2c-91e9064e8452",
|
|
51
|
+
"name": "sas_sql_tool",
|
|
52
|
+
"description": "",
|
|
53
|
+
"options": {
|
|
54
|
+
"includeDependencies": "true",
|
|
55
|
+
"includeRules": "true"
|
|
56
|
+
},
|
|
57
|
+
"requestedItems": [
|
|
58
|
+
"/jobDefinitions/definitions/60b26347-9342-4b19-a3bb-cce626331148"
|
|
59
|
+
],
|
|
60
|
+
"transferObjectCount": 2,
|
|
61
|
+
"transferDetails": [
|
|
62
|
+
{
|
|
63
|
+
"transferObject": {
|
|
64
|
+
"links": [
|
|
65
|
+
{
|
|
66
|
+
"method": "GET",
|
|
67
|
+
"rel": "self",
|
|
68
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects/9d478e7a-1eac-4735-8ecd-707c83990561",
|
|
69
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects/9d478e7a-1eac-4735-8ecd-707c83990561",
|
|
70
|
+
"type": "application/vnd.sas.transfer.object"
|
|
71
|
+
}
|
|
72
|
+
],
|
|
73
|
+
"version": 2,
|
|
74
|
+
"id": "9d478e7a-1eac-4735-8ecd-707c83990561",
|
|
75
|
+
"summary": {
|
|
76
|
+
"creationTimeStamp": "2025-11-22T18:20:27.979Z",
|
|
77
|
+
"modifiedTimeStamp": "2025-11-22T18:20:27.987Z",
|
|
78
|
+
"createdBy": "deva.kumar@sas.com",
|
|
79
|
+
"modifiedBy": "deva.kumar@sas.com",
|
|
80
|
+
"id": "60b26347-9342-4b19-a3bb-cce626331148",
|
|
81
|
+
"type": "jobDefinition",
|
|
82
|
+
"name": "sas_sql_tool",
|
|
83
|
+
"links": [
|
|
84
|
+
{
|
|
85
|
+
"method": "GET",
|
|
86
|
+
"rel": "self",
|
|
87
|
+
"href": "/jobDefinitions/definitions/60b26347-9342-4b19-a3bb-cce626331148",
|
|
88
|
+
"uri": "/jobDefinitions/definitions/60b26347-9342-4b19-a3bb-cce626331148",
|
|
89
|
+
"type": "application/vnd.sas.job.definition"
|
|
90
|
+
}
|
|
91
|
+
],
|
|
92
|
+
"version": 2
|
|
93
|
+
},
|
|
94
|
+
"content": "eyJjcmVhdGlvblRpbWVTdGFtcCI6IjIwMjUtMTEtMjJUMTg6MjA6MjcuOTc5WiIsIm1vZGlmaWVkVGltZVN0YW1wIjoiMjAyNS0xMS0yMlQxODoyMDoyNy45ODdaIiwiY3JlYXRlZEJ5IjoiZGV2YS5rdW1hckBzYXMuY29tIiwibW9kaWZpZWRCeSI6ImRldmEua3VtYXJAc2FzLmNvbSIsInZlcnNpb24iOjIsImlkIjoiNjBiMjYzNDctOTM0Mi00YjE5LWEzYmItY2NlNjI2MzMxMTQ4IiwibmFtZSI6InNhc19zcWxfdG9vbCIsInR5cGUiOiJDb21wdXRlIiwicGFyYW1ldGVycyI6W3sidmVyc2lvbiI6MSwibmFtZSI6Il9hY3Rpb24iLCJkZWZhdWx0VmFsdWUiOiJleGVjdXRlIiwidHlwZSI6ImNoYXJhY3RlciIsInJlcXVpcmVkIjpmYWxzZX0seyJ2ZXJzaW9uIjoxLCJuYW1lIjoiX2NvbnRleHROYW1lIiwiZGVmYXVsdFZhbHVlIjoiU0FTIFN0dWRpbyBjb21wdXRlIGNvbnRleHQiLCJ0eXBlIjoiQ0hBUkFDVEVSIiwibGFiZWwiOiJDb250ZXh0IE5hbWUiLCJyZXF1aXJlZCI6ZmFsc2V9XSwiY29kZSI6Ii8qIFNBUyB0ZW1wbGF0ZWQgY29kZSBnb2VzIGhlcmUgKlxyXG5cclxuZGF0YSBfbnVsbF87XHJcbiAgICBsZW5ndGggc3FsX2ZpbmFsICQ1MDAwLjtcclxuICAgICAgICBzcWxfZmluYWw9XCIlc3VwZXJxKHNxbClcIjtcclxuICAgICAgICBjYWxsIHN5bXB1dChcInNxbF9maW5hbFwiLHNxbF9maW5hbCk7ICAgICAgIFxyXG4gICAgcnVuO1xyXG5cclxuZmlsZW5hbWUgam91dHB1dCBmaWxlc3J2YyBwYXJlbnR1cmk9XCImU1lTX0pFU19KT0JfVVJJXCIgbmFtZT1cInF1ZXJ5X3Jlc3VsdHMuanNvblwiO1xyXG5cclxuJW1hY3JvIHJ1bl9zcWxfY29kZTsgICAgXHJcblxyXG4gICAgcHJvYyBzcWw7XHJcbiAgICAgY3JlYXRlIHRhYmxlIHdvcmsucXVlcnlfcmVzdWx0cyBhc1xyXG4gICAgICZzcWxfZmluYWwuO1xyXG4gICAgcXVpdDtcclxuXHJcbiAgICBwcm9jIHByaW50IGRhdGE9d29yay5xdWVyeV9yZXN1bHRzO3J1bjtcclxuICAgIFxyXG4gICAgcHJvYyBqc29uIG91dD0gam91dHB1dCBub3Nhc3RhZ3M7XHJcbiAgICBleHBvcnQgcXVlcnlfcmVzdWx0cztcclxuICAgIHJ1bjtcclxuJW1lbmQ7XHJcblxyXG4lcnVuX3NxbF9jb2RlO1xyXG4iLCJsaW5rcyI6W3sibWV0aG9kIjoiR0VUIiwicmVsIjoic2VsZiIsImhyZWYiOiIvam9iRGVmaW5pdGlvbnMvZGVmaW5pdGlvbnMvNjBiMjYzNDctOTM0Mi00YjE5LWEzYmItY2NlNjI2MzMxMTQ4IiwidXJpIjoiL2pvYkRlZmluaXRpb25zL2RlZmluaXRpb25zLzYwYjI2MzQ3LTkzNDItNGIxOS1hM2JiLWNjZTYyNjMzMTE0OCIsInR5cGUiOiJhcHBsaWNhdGlvbi92bmQuc2FzLmpvYi5kZWZpbml0aW9uIn0seyJtZXRob2QiOiJHRVQiLCJyZWwiOiJhbHRlcm5hdGUiLCJocmVmIjoiL2pvYkRlZmluaXRpb25zL2RlZmluaXRpb25zLzYwYjI2MzQ3LTkzNDItNGIxOS1hM2JiLWNjZTYyNjMzMTE0OCIsInVyaSI6Ii9qb2JEZWZpbml0aW9ucy9kZWZpbml0aW9ucy82MGIyNjM0Ny05MzQyLTRiMTktYTNiYi1jY2U2MjYzMzExNDgiLCJ0eXBlIjoiYXBwbGljYXRpb24vdm5kLnNhcy5zdW1tYXJ5In0seyJtZXRob2QiOiJQVVQiLCJyZWwiOiJ1cGRhdGUiLCJocmVmIjoiL2pvYkRlZmluaXRpb25zL2RlZmluaXRpb25zLzYwYjI2MzQ3LTkzNDItNGIxOS1hM2JiLWNjZTYyNjMzMTE0OCIsInVyaSI6Ii9qb2JEZWZpbml0aW9ucy9kZWZpbml0aW9ucy82MGIyNjM0Ny05MzQyLTRiMTktYTNiYi1jY2U2MjYzMzExNDgiLCJ0eXBlIjoiYXBwbGljYXRpb24vdm5kLnNhcy5qb2IuZGVmaW5pdGlvbiIsInJlc3BvbnNlVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuam9iLmRlZmluaXRpb24ifSx7Im1ldGhvZCI6IkRFTEVURSIsInJlbCI6ImRlbGV0ZSIsImhyZWYiOiIvam9iRGVmaW5pdGlvbnMvZGVmaW5pdGlvbnMvNjBiMjYzNDctOTM0Mi00YjE5LWEzYmItY2NlNjI2MzMxMTQ4IiwidXJpIjoiL2pvYkRlZmluaXRpb25zL2RlZmluaXRpb25zLzYwYjI2MzQ3LTkzNDItNGIxOS1hM2JiLWNjZTYyNjMzMTE0OCJ9XSwicHJvcGVydGllcyI6W3sibmFtZSI6InByb21wdHNfdjIiLCJ2YWx1ZSI6Intcblx0XCJzaG93UGFnZUNvbnRlbnRPbmx5XCI6IHRydWUsXG5cdFwicGFnZXNcIjogW1xuXHRcdHtcblx0XHRcdFwiaWRcIjogXCJwYWdlMVwiLFxuXHRcdFx0XCJsYWJlbFwiOiBcIlBhZ2UgMVwiLFxuXHRcdFx0XCJ0eXBlXCI6IFwicGFnZVwiLFxuXHRcdFx0XCJjaGlsZHJlblwiOiBbXG5cdFx0XHRcdHtcblx0XHRcdFx0XHRcImlkXCI6IFwic3FsXCIsXG5cdFx0XHRcdFx0XCJ0eXBlXCI6IFwidGV4dGZpZWxkXCIsXG5cdFx0XHRcdFx0XCJsYWJlbFwiOiBcInNxbFwiLFxuXHRcdFx0XHRcdFwicGxhY2Vob2xkZXJcIjogXCJcIixcblx0XHRcdFx0XHRcInJlcXVpcmVkXCI6IHRydWUsXG5cdFx0XHRcdFx0XCJ2aXNpYmxlXCI6IFwiXCJcblx0XHRcdFx0fVxuXHRcdFx0XVxuXHRcdH1cblx0XSxcblx0XCJzeW50YXh2ZXJzaW9uXCI6IFwiMS4zLjBcIixcblx0XCJ2YWx1ZXNcIjoge1xuXHRcdFwic3FsXCI6IFwiXCJcblx0fSxcblx0XCJwcm9tcHRIaWVyYXJjaGllc1wiOiBbXVxufSJ9XX0=",
|
|
95
|
+
"state": "enabled",
|
|
96
|
+
"contentSourceLocation": "/Public",
|
|
97
|
+
"connectors": [],
|
|
98
|
+
"substitutions": {}
|
|
99
|
+
},
|
|
100
|
+
"connectors": [
|
|
101
|
+
{
|
|
102
|
+
"id": "3ee54397-3d3e-4022-be6c-b5f2cd0508a6",
|
|
103
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
104
|
+
"relatedUris": [
|
|
105
|
+
"/jobDefinitions/definitions/60b26347-9342-4b19-a3bb-cce626331148"
|
|
106
|
+
],
|
|
107
|
+
"name": "Public",
|
|
108
|
+
"contentType": "application/vnd.sas.content.folder+json",
|
|
109
|
+
"type": "parentFolder",
|
|
110
|
+
"hints": {},
|
|
111
|
+
"links": [
|
|
112
|
+
{
|
|
113
|
+
"method": "GET",
|
|
114
|
+
"rel": "self",
|
|
115
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/connectors/3ee54397-3d3e-4022-be6c-b5f2cd0508a6",
|
|
116
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/connectors/3ee54397-3d3e-4022-be6c-b5f2cd0508a6",
|
|
117
|
+
"type": "application/vnd.sas.transfer.connector"
|
|
118
|
+
}
|
|
119
|
+
],
|
|
120
|
+
"forceMapping": false
|
|
121
|
+
}
|
|
122
|
+
]
|
|
123
|
+
},
|
|
124
|
+
{
|
|
125
|
+
"transferObject": {
|
|
126
|
+
"links": [
|
|
127
|
+
{
|
|
128
|
+
"method": "GET",
|
|
129
|
+
"rel": "self",
|
|
130
|
+
"href": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects/d9c5b8ad-002f-41ee-bb8d-92a870aa3c00",
|
|
131
|
+
"uri": "/transfer/packages/ad51d696-6a90-41ac-bb2c-91e9064e8452/objects/d9c5b8ad-002f-41ee-bb8d-92a870aa3c00",
|
|
132
|
+
"type": "application/vnd.sas.transfer.object"
|
|
133
|
+
}
|
|
134
|
+
],
|
|
135
|
+
"version": 2,
|
|
136
|
+
"id": "d9c5b8ad-002f-41ee-bb8d-92a870aa3c00",
|
|
137
|
+
"summary": {
|
|
138
|
+
"creationTimeStamp": "2025-11-19T21:23:48.557Z",
|
|
139
|
+
"modifiedTimeStamp": "2025-11-19T21:23:48.557Z",
|
|
140
|
+
"createdBy": "anonymous",
|
|
141
|
+
"modifiedBy": "anonymous",
|
|
142
|
+
"id": "31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
143
|
+
"type": "folder",
|
|
144
|
+
"name": "Public",
|
|
145
|
+
"description": "Public folder for general access.",
|
|
146
|
+
"links": [
|
|
147
|
+
{
|
|
148
|
+
"method": "GET",
|
|
149
|
+
"rel": "self",
|
|
150
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
151
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
152
|
+
"type": "application/vnd.sas.content.folder"
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
"method": "DELETE",
|
|
156
|
+
"rel": "delete",
|
|
157
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
158
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9"
|
|
159
|
+
},
|
|
160
|
+
{
|
|
161
|
+
"method": "DELETE",
|
|
162
|
+
"rel": "deleteRecursively",
|
|
163
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9?recursive=true",
|
|
164
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9?recursive=true"
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"method": "GET",
|
|
168
|
+
"rel": "members",
|
|
169
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members",
|
|
170
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members",
|
|
171
|
+
"type": "application/vnd.sas.collection",
|
|
172
|
+
"itemType": "application/vnd.sas.content.folder"
|
|
173
|
+
},
|
|
174
|
+
{
|
|
175
|
+
"method": "POST",
|
|
176
|
+
"rel": "addMember",
|
|
177
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members",
|
|
178
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members",
|
|
179
|
+
"type": "application/vnd.sas.content.folder.member",
|
|
180
|
+
"responseType": "application/vnd.sas.content.folder.member"
|
|
181
|
+
},
|
|
182
|
+
{
|
|
183
|
+
"method": "GET",
|
|
184
|
+
"rel": "ancestors",
|
|
185
|
+
"href": "/folders/ancestors?childUri=/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
186
|
+
"uri": "/folders/ancestors?childUri=/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
187
|
+
"type": "application/vnd.sas.content.folder.ancestor"
|
|
188
|
+
},
|
|
189
|
+
{
|
|
190
|
+
"method": "POST",
|
|
191
|
+
"rel": "createChild",
|
|
192
|
+
"href": "/folders/folders?parentFolderUri=/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
193
|
+
"uri": "/folders/folders?parentFolderUri=/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
194
|
+
"type": "application/vnd.sas.content.folder"
|
|
195
|
+
},
|
|
196
|
+
{
|
|
197
|
+
"method": "PUT",
|
|
198
|
+
"rel": "validateNewMemberName",
|
|
199
|
+
"href": "/folders/commons/validations/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members/@new/name?value={newname}&type={newtype}",
|
|
200
|
+
"uri": "/folders/commons/validations/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9/members/@new/name?value={newname}&type={newtype}",
|
|
201
|
+
"type": "application/vnd.sas.validation"
|
|
202
|
+
},
|
|
203
|
+
{
|
|
204
|
+
"method": "GET",
|
|
205
|
+
"rel": "transferExport",
|
|
206
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
207
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
208
|
+
"responseType": "application/vnd.sas.transfer.object"
|
|
209
|
+
},
|
|
210
|
+
{
|
|
211
|
+
"method": "PUT",
|
|
212
|
+
"rel": "transferImportUpdate",
|
|
213
|
+
"href": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
214
|
+
"uri": "/folders/folders/31702e8e-acbe-4f7d-9322-fcb6245ba3c9",
|
|
215
|
+
"type": "application/vnd.sas.transfer.object",
|
|
216
|
+
"responseType": "application/vnd.sas.summary"
|
|
217
|
+
},
|
|
218
|
+
{
|
|
219
|
+
"method": "POST",
|
|
220
|
+
"rel": "transferImport",
|
|
221
|
+
"href": "/folders/folders",
|
|
222
|
+
"uri": "/folders/folders",
|
|
223
|
+
"type": "application/vnd.sas.transfer.object",
|
|
224
|
+
"responseType": "application/vnd.sas.summary"
|
|
225
|
+
}
|
|
226
|
+
],
|
|
227
|
+
"version": 2
|
|
228
|
+
},
|
|
229
|
+
"content": "eyJmb2xkZXIiOnsiY3JlYXRpb25UaW1lU3RhbXAiOiIyMDI1LTExLTE5VDIxOjIzOjQ4LjU1Nzc4OVoiLCJjcmVhdGVkQnkiOiJhbm9ueW1vdXMiLCJtb2RpZmllZFRpbWVTdGFtcCI6IjIwMjUtMTEtMTlUMjE6MjM6NDguNTU3NzlaIiwibW9kaWZpZWRCeSI6ImFub255bW91cyIsInZlcnNpb24iOjEsImlkIjoiMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwibmFtZSI6IlB1YmxpYyIsImRlc2NyaXB0aW9uIjoiUHVibGljIGZvbGRlciBmb3IgZ2VuZXJhbCBhY2Nlc3MuIiwidHlwZSI6ImZvbGRlciIsIm1lbWJlckNvdW50IjoxLCJwcm9wZXJ0aWVzIjp7ImFsbG93TW92ZSI6ImZhbHNlIiwidXVpZCI6IjkzNTg0MmRhLTliYTAtNDc5Yy05OWUzLTZhNGU0ODkzMjJmYyJ9LCJsaW5rcyI6W3sibWV0aG9kIjoiR0VUIiwicmVsIjoic2VsZiIsImhyZWYiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOSIsInVyaSI6Ii9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwidHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuY29udGVudC5mb2xkZXIifSx7Im1ldGhvZCI6IkRFTEVURSIsInJlbCI6ImRlbGV0ZSIsImhyZWYiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOSIsInVyaSI6Ii9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5In0seyJtZXRob2QiOiJERUxFVEUiLCJyZWwiOiJkZWxldGVSZWN1cnNpdmVseSIsImhyZWYiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOT9yZWN1cnNpdmU9dHJ1ZSIsInVyaSI6Ii9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5P3JlY3Vyc2l2ZT10cnVlIn0seyJtZXRob2QiOiJHRVQiLCJyZWwiOiJtZW1iZXJzIiwiaHJlZiI6Ii9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5L21lbWJlcnMiLCJ1cmkiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOS9tZW1iZXJzIiwidHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuY29sbGVjdGlvbiIsIml0ZW1UeXBlIjoiYXBwbGljYXRpb24vdm5kLnNhcy5jb250ZW50LmZvbGRlciJ9LHsibWV0aG9kIjoiUE9TVCIsInJlbCI6ImFkZE1lbWJlciIsImhyZWYiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOS9tZW1iZXJzIiwidXJpIjoiL2ZvbGRlcnMvZm9sZGVycy8zMTcwMmU4ZS1hY2JlLTRmN2QtOTMyMi1mY2I2MjQ1YmEzYzkvbWVtYmVycyIsInR5cGUiOiJhcHBsaWNhdGlvbi92bmQuc2FzLmNvbnRlbnQuZm9sZGVyLm1lbWJlciIsInJlc3BvbnNlVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuY29udGVudC5mb2xkZXIubWVtYmVyIn0seyJtZXRob2QiOiJHRVQiLCJyZWwiOiJhbmNlc3RvcnMiLCJocmVmIjoiL2ZvbGRlcnMvYW5jZXN0b3JzP2NoaWxkVXJpPS9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwidXJpIjoiL2ZvbGRlcnMvYW5jZXN0b3JzP2NoaWxkVXJpPS9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwidHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuY29udGVudC5mb2xkZXIuYW5jZXN0b3IifSx7Im1ldGhvZCI6IlBPU1QiLCJyZWwiOiJjcmVhdGVDaGlsZCIsImhyZWYiOiIvZm9sZGVycy9mb2xkZXJzP3BhcmVudEZvbGRlclVyaT0vZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOSIsInVyaSI6Ii9mb2xkZXJzL2ZvbGRlcnM/cGFyZW50Rm9sZGVyVXJpPS9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwidHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuY29udGVudC5mb2xkZXIifSx7Im1ldGhvZCI6IlBVVCIsInJlbCI6InZhbGlkYXRlTmV3TWVtYmVyTmFtZSIsImhyZWYiOiIvZm9sZGVycy9jb21tb25zL3ZhbGlkYXRpb25zL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5L21lbWJlcnMvQG5ldy9uYW1lP3ZhbHVlPXtuZXduYW1lfVx1MDAyNnR5cGU9e25ld3R5cGV9IiwidXJpIjoiL2ZvbGRlcnMvY29tbW9ucy92YWxpZGF0aW9ucy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOS9tZW1iZXJzL0BuZXcvbmFtZT92YWx1ZT17bmV3bmFtZX1cdTAwMjZ0eXBlPXtuZXd0eXBlfSIsInR5cGUiOiJhcHBsaWNhdGlvbi92bmQuc2FzLnZhbGlkYXRpb24ifSx7Im1ldGhvZCI6IkdFVCIsInJlbCI6InRyYW5zZmVyRXhwb3J0IiwiaHJlZiI6Ii9mb2xkZXJzL2ZvbGRlcnMvMzE3MDJlOGUtYWNiZS00ZjdkLTkzMjItZmNiNjI0NWJhM2M5IiwidXJpIjoiL2ZvbGRlcnMvZm9sZGVycy8zMTcwMmU4ZS1hY2JlLTRmN2QtOTMyMi1mY2I2MjQ1YmEzYzkiLCJyZXNwb25zZVR5cGUiOiJhcHBsaWNhdGlvbi92bmQuc2FzLnRyYW5zZmVyLm9iamVjdCJ9LHsibWV0aG9kIjoiUFVUIiwicmVsIjoidHJhbnNmZXJJbXBvcnRVcGRhdGUiLCJocmVmIjoiL2ZvbGRlcnMvZm9sZGVycy8zMTcwMmU4ZS1hY2JlLTRmN2QtOTMyMi1mY2I2MjQ1YmEzYzkiLCJ1cmkiOiIvZm9sZGVycy9mb2xkZXJzLzMxNzAyZThlLWFjYmUtNGY3ZC05MzIyLWZjYjYyNDViYTNjOSIsInR5cGUiOiJhcHBsaWNhdGlvbi92bmQuc2FzLnRyYW5zZmVyLm9iamVjdCIsInJlc3BvbnNlVHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMuc3VtbWFyeSJ9LHsibWV0aG9kIjoiUE9TVCIsInJlbCI6InRyYW5zZmVySW1wb3J0IiwiaHJlZiI6Ii9mb2xkZXJzL2ZvbGRlcnMiLCJ1cmkiOiIvZm9sZGVycy9mb2xkZXJzIiwidHlwZSI6ImFwcGxpY2F0aW9uL3ZuZC5zYXMudHJhbnNmZXIub2JqZWN0IiwicmVzcG9uc2VUeXBlIjoiYXBwbGljYXRpb24vdm5kLnNhcy5zdW1tYXJ5In1dfX0=",
|
|
230
|
+
"state": "enabled",
|
|
231
|
+
"connectors": [],
|
|
232
|
+
"substitutions": {}
|
|
233
|
+
},
|
|
234
|
+
"connectors": []
|
|
235
|
+
}
|
|
236
|
+
]
|
|
237
|
+
}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
#
|
|
3
|
+
# Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
4
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
#
|
|
6
|
+
# Requirement: KUBECONFIG must be set to point to the target cluster
|
|
7
|
+
# The namespace is assumed to be 'viya'
|
|
8
|
+
kubectl -n viya get secret sas-viya-ca-certificate-secret -o go-template='{{(index .data "ca.crt")}}' | base64 -d > $HOME/viyaCert/ca.pem
|
package/src/core.js
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/*
|
|
3
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
import corehttp from './corehttp.js';
|
|
7
|
+
|
|
8
|
+
async function core(cache,mcpType,appEnvBase) {
|
|
9
|
+
if (mcpType === 'http') {
|
|
10
|
+
console.error('[Note]MCP Server starting with HTTP transport');
|
|
11
|
+
await corehttp(cache, appEnvBase);
|
|
12
|
+
} else {
|
|
13
|
+
console.error('[Note] MCP Server starting with stdio transport');
|
|
14
|
+
let mcpServer = cache.get("mcpServer");
|
|
15
|
+
await createMcpTransport(mcpServer,mcpType,appEnvBase);
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export default core;
|
package/src/coreSSE.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
|
|
2
|
+
/*
|
|
3
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
4
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
8
|
+
async function coreSSE(mcpServer) {
|
|
9
|
+
let transport = new StdioServerTransport();
|
|
10
|
+
await mcpServer.connect(transport);
|
|
11
|
+
console.error("[Note] MCP Server connected over stdio transport.");
|
|
12
|
+
return transport;
|
|
13
|
+
}
|
|
14
|
+
export default coreSSE;
|
package/src/corehttp.js
ADDED
|
@@ -0,0 +1,335 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
import express from "express";
|
|
6
|
+
|
|
7
|
+
import https from "https";
|
|
8
|
+
import cors from "cors";
|
|
9
|
+
//import rateLimit from "express-rate-limit";
|
|
10
|
+
//import helmet from "helmet";
|
|
11
|
+
import bodyParser from "body-parser";
|
|
12
|
+
|
|
13
|
+
import selfsigned from "selfsigned";
|
|
14
|
+
import getOpts from "./toolHelpers/getOpts.js";
|
|
15
|
+
import fs from "fs";
|
|
16
|
+
|
|
17
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
18
|
+
import { randomUUID } from "node:crypto";
|
|
19
|
+
import { isInitializeRequest } from "@modelcontextprotocol/sdk/types.js";
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
// setup express server
|
|
23
|
+
|
|
24
|
+
async function corehttp(mcpServer, cache, currentAppEnvContext) {
|
|
25
|
+
// setup for change to persistence session
|
|
26
|
+
let headerCache = {};
|
|
27
|
+
|
|
28
|
+
const app = express();
|
|
29
|
+
|
|
30
|
+
app.use(express.json({ limit: "50mb" }));
|
|
31
|
+
app.use(
|
|
32
|
+
cors({
|
|
33
|
+
origin: "*",
|
|
34
|
+
credentials: false,
|
|
35
|
+
exposedHeaders: ["mcp-session-id"],
|
|
36
|
+
allowedHeaders: [
|
|
37
|
+
"Accept",
|
|
38
|
+
"Authorization",
|
|
39
|
+
"Content-Type",
|
|
40
|
+
"If-None-Match",
|
|
41
|
+
"Accept-language",
|
|
42
|
+
"mcp-session-id",
|
|
43
|
+
],
|
|
44
|
+
})
|
|
45
|
+
);
|
|
46
|
+
// app.use(helmet());
|
|
47
|
+
app.use(bodyParser.json({ limit: process.env.JSON_LIMIT ?? "50mb" }));
|
|
48
|
+
|
|
49
|
+
// setup routes
|
|
50
|
+
app.get("/health", (req, res) => {
|
|
51
|
+
console.error("Received request for health endpoint");
|
|
52
|
+
|
|
53
|
+
res.json({
|
|
54
|
+
name: "@sassoftware/mcp-server",
|
|
55
|
+
version: "1.0.0",
|
|
56
|
+
description: "SAS Viya Sample MCP Server",
|
|
57
|
+
endpoints: {
|
|
58
|
+
mcp: "/mcp",
|
|
59
|
+
health: "/health",
|
|
60
|
+
},
|
|
61
|
+
usage:
|
|
62
|
+
"Use with MCP Inspector or compatible MCP clients like vscode or your own MCP client",
|
|
63
|
+
});
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
// Root endpoint info
|
|
67
|
+
|
|
68
|
+
app.get("/", (req, res) => {
|
|
69
|
+
res.json({
|
|
70
|
+
name: "SAS Viya Sample MCP Server",
|
|
71
|
+
version: "1.0.0",
|
|
72
|
+
description: "SAS Viya Sample MCP Server",
|
|
73
|
+
endpoints: {
|
|
74
|
+
mcp: "/mcp",
|
|
75
|
+
health: "/health",
|
|
76
|
+
},
|
|
77
|
+
usage: "Use with MCP Inspector or compatible MCP clients",
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
// api metadata endpoint
|
|
82
|
+
app.get("/apiMeta", (req, res) => {
|
|
83
|
+
let spec = fs.readFileSync("./openApi.json", "utf8");
|
|
84
|
+
let specJson = JSON.parse(spec);
|
|
85
|
+
res.json(specJson);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
// handle processing of information in header.
|
|
89
|
+
function requireBearer(req, res, next) {
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
// process any new header information
|
|
93
|
+
|
|
94
|
+
// Allow different VIYA server per sessionid(user)
|
|
95
|
+
let headerCache = {};
|
|
96
|
+
if (req.header("X-VIYA-SERVER") != null) {
|
|
97
|
+
console.error("[Note] Using user supplied VIYA server");
|
|
98
|
+
headerCache.VIYA_SERVER = req.header("X-VIYA-SERVER");
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// used when doing autorization via mcp client
|
|
102
|
+
// ideal for production use
|
|
103
|
+
const hdr = req.header("Authorization");
|
|
104
|
+
if (hdr != null) {
|
|
105
|
+
headerCache.bearerToken = hdr.slice(7);
|
|
106
|
+
headerCache.AUTHFLOW = "bearer";
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
// faking out api key since Viya does not support
|
|
110
|
+
// not ideal for production
|
|
111
|
+
const hdr2 = req.header("X-REFRESH-TOKEN");
|
|
112
|
+
if (hdr2 != null) {
|
|
113
|
+
headerCache.refreshToken = hdr2;
|
|
114
|
+
headerCache.AUTHFLOW = "refresh";
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
next();
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
// process mcp endpoint requests
|
|
121
|
+
const handleRequest = async (req, res) => {
|
|
122
|
+
let transport;
|
|
123
|
+
let transports = cache.get("transports");
|
|
124
|
+
try {
|
|
125
|
+
|
|
126
|
+
let sessionId = req.headers["mcp-session-id"];
|
|
127
|
+
|
|
128
|
+
// we have session id, get existing transport
|
|
129
|
+
|
|
130
|
+
if (sessionId != null) {
|
|
131
|
+
/* existing transport */
|
|
132
|
+
transport = transports[sessionId];
|
|
133
|
+
if (transport == null) {
|
|
134
|
+
throw new Error(`No transport found for session ID: ${sessionId}`);
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
// post the curren session - used to pass _appContext to tools
|
|
138
|
+
cache.set("currentId", sessionId);
|
|
139
|
+
|
|
140
|
+
// get app context for session
|
|
141
|
+
let _appContext = cache.get(sessionId);
|
|
142
|
+
|
|
143
|
+
//if first prompt on a sessionid, create app context
|
|
144
|
+
if (_appContext == null) {
|
|
145
|
+
|
|
146
|
+
let appEnvTemplate = cache.get("appEnvTemplate");
|
|
147
|
+
_appContext = Object.assign({}, appEnvTemplate, headerCache);
|
|
148
|
+
cache.set(sessionId, _appContext);
|
|
149
|
+
}
|
|
150
|
+
console.error("[Note] Using existing transport for session ID:", sessionId);
|
|
151
|
+
|
|
152
|
+
await transport.handleRequest(req, res, req.body);
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
// initialize request
|
|
156
|
+
else if (!sessionId && isInitializeRequest(req.body)) {
|
|
157
|
+
// create transport
|
|
158
|
+
|
|
159
|
+
transport = new StreamableHTTPServerTransport({
|
|
160
|
+
sessionIdGenerator: () => randomUUID(),
|
|
161
|
+
enableJsonResponse: true,
|
|
162
|
+
onsessioninitialized: (sessionId) => {
|
|
163
|
+
// Store the transport by session ID
|
|
164
|
+
transports[sessionId] = transport;
|
|
165
|
+
},
|
|
166
|
+
});
|
|
167
|
+
// Clean up transport when closed
|
|
168
|
+
transport.onclose = () => {
|
|
169
|
+
if (transport.sessionId) {
|
|
170
|
+
delete transports[transport.sessionId];
|
|
171
|
+
}
|
|
172
|
+
};
|
|
173
|
+
console.error("[Note] Connecting mcpServer to new transport...");
|
|
174
|
+
await mcpServer.connect(transport);
|
|
175
|
+
|
|
176
|
+
// Save transport data and app context for use in tools
|
|
177
|
+
|
|
178
|
+
await transport.handleRequest(req, res, req.body);
|
|
179
|
+
// cache transport
|
|
180
|
+
cache.set("transports", transports);
|
|
181
|
+
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
catch (error) {
|
|
185
|
+
console.error("Error handling MCP request:", error);
|
|
186
|
+
if (!res.headersSent) {
|
|
187
|
+
res.status(500).json({
|
|
188
|
+
jsonrpc: "2.0",
|
|
189
|
+
error: {
|
|
190
|
+
code: -32603,
|
|
191
|
+
message: JSON.stringify(error),
|
|
192
|
+
},
|
|
193
|
+
id: null,
|
|
194
|
+
});
|
|
195
|
+
}
|
|
196
|
+
return;
|
|
197
|
+
}
|
|
198
|
+
};
|
|
199
|
+
const handleGetDelete = async (req, res) => {
|
|
200
|
+
console.error(req.method, "/mcp called");
|
|
201
|
+
const sessionId = req.headers["mcp-session-id"];
|
|
202
|
+
console.error("Handling GET/DELETE for session ID:", sessionId);
|
|
203
|
+
let transports = cache.get("transports");
|
|
204
|
+
let transport = transports[sessionId];
|
|
205
|
+
if (!sessionId || transport == null) {
|
|
206
|
+
res.status(400).send(`[Error] In ${req.method}: Invalid or missing session ID ${sessionId}`);
|
|
207
|
+
return;
|
|
208
|
+
}
|
|
209
|
+
await transport.handleRequest(req, res);
|
|
210
|
+
if (req.method === "DELETE") {
|
|
211
|
+
console.error("Deleting transport and cache for session ID:", sessionId);
|
|
212
|
+
delete transports[sessionId];
|
|
213
|
+
cache.del(sessionId);
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
app.options("/mcp", (_, res) => res.sendStatus(204));
|
|
218
|
+
app.post("/mcp", requireBearer, handleRequest);
|
|
219
|
+
app.get("/mcp", handleGetDelete);
|
|
220
|
+
app.delete("/mcp", handleGetDelete);
|
|
221
|
+
|
|
222
|
+
// Start the server
|
|
223
|
+
let appEnvBase = cache.get("appEnvBase");
|
|
224
|
+
|
|
225
|
+
const PORT = appEnvBase.PORT;
|
|
226
|
+
|
|
227
|
+
// get user specified TLS options
|
|
228
|
+
let appServer;
|
|
229
|
+
|
|
230
|
+
// get TLS options
|
|
231
|
+
if (appEnvBase.HTTPS === true) {
|
|
232
|
+
appEnvBase.tlsOpts = getOpts(appEnvBase);
|
|
233
|
+
if (appEnvBase.tlsOpts == null) {
|
|
234
|
+
appEnvBase.tlsOpts = await getTls(appEnvBase);
|
|
235
|
+
appEnvBase.tlsOpts.requestCert = false;
|
|
236
|
+
appEnvBase.tlsOpts.rejectUnauthorized = false;
|
|
237
|
+
}
|
|
238
|
+
cache.set("appEnvBase", appEnvBase);
|
|
239
|
+
|
|
240
|
+
console.error(`[Note] MCP Server listening on port ${PORT}`);
|
|
241
|
+
console.error(
|
|
242
|
+
"[Note] Visit https://localhost:8080/health for health check"
|
|
243
|
+
);
|
|
244
|
+
console.error(
|
|
245
|
+
"[Note] Configure your mcp host to use https://localhost:8080/mcp to interact with the MCP server"
|
|
246
|
+
);
|
|
247
|
+
console.error("[Note] Press Ctrl+C to stop the server");
|
|
248
|
+
|
|
249
|
+
appServer = https.createServer(appEnvBase.tlsOpts, app);
|
|
250
|
+
appServer.listen(PORT, "0.0.0.0", () => {});
|
|
251
|
+
} else {
|
|
252
|
+
console.error(`[Note] MCP Server listening on port ${PORT}`);
|
|
253
|
+
console.error("[Note] Visit http://localhost:8080/health for health check");
|
|
254
|
+
console.error(
|
|
255
|
+
"[Note] Configure your mcp host to use http://localhost:8080/mcp to interact with the MCP server"
|
|
256
|
+
);
|
|
257
|
+
console.error("[Note] Press Ctrl+C to stop the server");
|
|
258
|
+
|
|
259
|
+
appServer = app.listen(PORT, "0.0.0.0", () => {
|
|
260
|
+
console.error(
|
|
261
|
+
`[Note] Express server successfully bound to 0.0.0.0:${PORT}`
|
|
262
|
+
);
|
|
263
|
+
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
process.on("SIGTERM", () => {
|
|
267
|
+
console.error("Server closed");
|
|
268
|
+
if (appServer != null) {
|
|
269
|
+
appServer.close(() => {});
|
|
270
|
+
}
|
|
271
|
+
process.exit(0);
|
|
272
|
+
});
|
|
273
|
+
process.on("SIGINT", () => {
|
|
274
|
+
console.error("Server closed");
|
|
275
|
+
if (appServer != null) {
|
|
276
|
+
appServer.close(() => {});
|
|
277
|
+
}
|
|
278
|
+
process.exit(0);
|
|
279
|
+
});
|
|
280
|
+
|
|
281
|
+
// create unsigned TLS cert
|
|
282
|
+
async function getTls(appEnv) {
|
|
283
|
+
let tlscreate =
|
|
284
|
+
appEnv.TLS_CREATE == null
|
|
285
|
+
? "TLS_CREATE=C:US,ST:NC,L:Cary,O:SAS Institute,OU:STO,CN:localhost,ALT:na.sas.com"
|
|
286
|
+
: appEnv.TLS_CREATE;
|
|
287
|
+
let subjt = tlscreate.replaceAll('"', "").trim();
|
|
288
|
+
let subj = subjt.split(",");
|
|
289
|
+
|
|
290
|
+
let d = {};
|
|
291
|
+
subj.map((c) => {
|
|
292
|
+
let r = c.split(":");
|
|
293
|
+
d[r[0]] = r[1];
|
|
294
|
+
return { value: r[1] };
|
|
295
|
+
});
|
|
296
|
+
|
|
297
|
+
let attr = [
|
|
298
|
+
{
|
|
299
|
+
name: "commonName",
|
|
300
|
+
value: d.CN,
|
|
301
|
+
},
|
|
302
|
+
{
|
|
303
|
+
name: "countryName",
|
|
304
|
+
value: d.C,
|
|
305
|
+
},
|
|
306
|
+
{
|
|
307
|
+
shortName: "ST",
|
|
308
|
+
value: d.ST,
|
|
309
|
+
},
|
|
310
|
+
{
|
|
311
|
+
name: "localityName",
|
|
312
|
+
value: d.L,
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
name: "organizationName",
|
|
316
|
+
value: d.O,
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
shortName: "OU",
|
|
320
|
+
value: d.OU,
|
|
321
|
+
},
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
let pems = selfsigned.generate(attr);
|
|
325
|
+
// selfsigned generates a new keypair
|
|
326
|
+
let tls = {
|
|
327
|
+
cert: pems.cert,
|
|
328
|
+
key: pems.private,
|
|
329
|
+
};
|
|
330
|
+
console.error("Generated self-signed TLS certificate");
|
|
331
|
+
return tls;
|
|
332
|
+
}
|
|
333
|
+
}
|
|
334
|
+
|
|
335
|
+
export default corehttp;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright © 2025, SAS Institute Inc., Cary, NC, USA. All Rights Reserved.
|
|
3
|
+
* SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
*/
|
|
5
|
+
|
|
6
|
+
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
|
|
7
|
+
import { randomUUID } from "node:crypto";
|
|
8
|
+
|
|
9
|
+
async function createHttpTransport( mcpServer) {
|
|
10
|
+
|
|
11
|
+
let transport = null;
|
|
12
|
+
try {
|
|
13
|
+
transport = new StreamableHTTPServerTransport({
|
|
14
|
+
sessionIdGenerator: () => randomUUID(),
|
|
15
|
+
enableJsonResponse: true,
|
|
16
|
+
});
|
|
17
|
+
console.error("Connecting mcpServer to transport", transport);
|
|
18
|
+
await mcpServer.connect(transport);
|
|
19
|
+
console.error("Successfully connected to the mcp server");
|
|
20
|
+
} catch (error) {
|
|
21
|
+
console.error("Error connecting mcpServer to transport:", error);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
return transport;
|
|
25
|
+
}
|
|
26
|
+
export default createHttpTransport;
|