fastmcp 3.20.1 → 3.20.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/package.json +4 -1
- package/.github/workflows/feature.yaml +0 -39
- package/.github/workflows/main.yaml +0 -50
- package/.prettierignore +0 -1
- package/.roo/mcp.json +0 -11
- package/eslint.config.ts +0 -14
- package/jsr.json +0 -7
- package/src/FastMCP.oauth.test.ts +0 -225
- package/src/FastMCP.session-context.test.ts +0 -136
- package/src/FastMCP.session-id.test.ts +0 -359
- package/src/FastMCP.test.ts +0 -4389
- package/src/FastMCP.ts +0 -2548
- package/src/bin/fastmcp.ts +0 -191
- package/src/examples/addition.ts +0 -333
- package/src/examples/custom-logger.ts +0 -201
- package/src/examples/oauth-server.ts +0 -113
- package/src/examples/session-context.ts +0 -269
- package/src/examples/session-id-counter.ts +0 -230
- package/tsconfig.json +0 -8
- package/vitest.config.js +0 -9
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "fastmcp",
|
|
3
|
-
"version": "3.20.
|
|
3
|
+
"version": "3.20.2",
|
|
4
4
|
"main": "dist/FastMCP.js",
|
|
5
5
|
"scripts": {
|
|
6
6
|
"build": "tsup",
|
|
@@ -52,6 +52,9 @@
|
|
|
52
52
|
"@sebbo2002/semantic-release-jsr"
|
|
53
53
|
]
|
|
54
54
|
},
|
|
55
|
+
"files": [
|
|
56
|
+
"dist"
|
|
57
|
+
],
|
|
55
58
|
"devDependencies": {
|
|
56
59
|
"@eslint/js": "^9.33.0",
|
|
57
60
|
"@modelcontextprotocol/inspector": "^0.16.2",
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
name: Run Tests
|
|
2
|
-
on:
|
|
3
|
-
pull_request:
|
|
4
|
-
branches:
|
|
5
|
-
- main
|
|
6
|
-
types:
|
|
7
|
-
- opened
|
|
8
|
-
- synchronize
|
|
9
|
-
- reopened
|
|
10
|
-
- ready_for_review
|
|
11
|
-
jobs:
|
|
12
|
-
test:
|
|
13
|
-
runs-on: ubuntu-latest
|
|
14
|
-
name: Test
|
|
15
|
-
strategy:
|
|
16
|
-
fail-fast: true
|
|
17
|
-
matrix:
|
|
18
|
-
node:
|
|
19
|
-
- 22
|
|
20
|
-
steps:
|
|
21
|
-
- name: Checkout repository
|
|
22
|
-
uses: actions/checkout@v4
|
|
23
|
-
with:
|
|
24
|
-
fetch-depth: 0
|
|
25
|
-
- uses: pnpm/action-setup@v4
|
|
26
|
-
with:
|
|
27
|
-
version: 9
|
|
28
|
-
- name: Setup NodeJS ${{ matrix.node }}
|
|
29
|
-
uses: actions/setup-node@v4
|
|
30
|
-
with:
|
|
31
|
-
node-version: ${{ matrix.node }}
|
|
32
|
-
cache: "pnpm"
|
|
33
|
-
cache-dependency-path: "**/pnpm-lock.yaml"
|
|
34
|
-
- name: Install dependencies
|
|
35
|
-
run: pnpm install
|
|
36
|
-
- name: Run lint
|
|
37
|
-
run: pnpm lint
|
|
38
|
-
- name: Run tests
|
|
39
|
-
run: pnpm test
|
|
@@ -1,50 +0,0 @@
|
|
|
1
|
-
name: Release
|
|
2
|
-
on:
|
|
3
|
-
push:
|
|
4
|
-
branches:
|
|
5
|
-
- main
|
|
6
|
-
jobs:
|
|
7
|
-
test:
|
|
8
|
-
environment: release
|
|
9
|
-
name: Test
|
|
10
|
-
strategy:
|
|
11
|
-
fail-fast: true
|
|
12
|
-
matrix:
|
|
13
|
-
node:
|
|
14
|
-
- 22
|
|
15
|
-
runs-on: ubuntu-latest
|
|
16
|
-
permissions:
|
|
17
|
-
contents: write
|
|
18
|
-
id-token: write
|
|
19
|
-
steps:
|
|
20
|
-
- name: setup repository
|
|
21
|
-
uses: actions/checkout@v4
|
|
22
|
-
with:
|
|
23
|
-
fetch-depth: 0
|
|
24
|
-
- uses: pnpm/action-setup@v4
|
|
25
|
-
with:
|
|
26
|
-
version: 9
|
|
27
|
-
- name: setup node.js
|
|
28
|
-
uses: actions/setup-node@v4
|
|
29
|
-
with:
|
|
30
|
-
cache: "pnpm"
|
|
31
|
-
node-version: ${{ matrix.node }}
|
|
32
|
-
- name: Setup NodeJS ${{ matrix.node }}
|
|
33
|
-
uses: actions/setup-node@v4
|
|
34
|
-
with:
|
|
35
|
-
node-version: ${{ matrix.node }}
|
|
36
|
-
cache: "pnpm"
|
|
37
|
-
cache-dependency-path: "**/pnpm-lock.yaml"
|
|
38
|
-
- name: Install dependencies
|
|
39
|
-
run: pnpm install
|
|
40
|
-
- name: Run lint
|
|
41
|
-
run: pnpm lint
|
|
42
|
-
- name: Run tests
|
|
43
|
-
run: pnpm test
|
|
44
|
-
- name: Build
|
|
45
|
-
run: pnpm build
|
|
46
|
-
- name: Release
|
|
47
|
-
run: pnpm semantic-release
|
|
48
|
-
env:
|
|
49
|
-
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
50
|
-
NPM_TOKEN: ${{ secrets.NPM_TOKEN }}
|
package/.prettierignore
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
pnpm-lock.yaml
|
package/.roo/mcp.json
DELETED
package/eslint.config.ts
DELETED
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
import eslint from "@eslint/js";
|
|
2
|
-
import eslintConfigPrettier from "eslint-config-prettier/flat";
|
|
3
|
-
import perfectionist from "eslint-plugin-perfectionist";
|
|
4
|
-
import tseslint from "typescript-eslint";
|
|
5
|
-
|
|
6
|
-
export default tseslint.config(
|
|
7
|
-
eslint.configs.recommended,
|
|
8
|
-
tseslint.configs.recommended,
|
|
9
|
-
perfectionist.configs["recommended-alphabetical"],
|
|
10
|
-
eslintConfigPrettier,
|
|
11
|
-
{
|
|
12
|
-
ignores: ["**/*.js", "dist/**"],
|
|
13
|
-
},
|
|
14
|
-
);
|
package/jsr.json
DELETED
|
@@ -1,225 +0,0 @@
|
|
|
1
|
-
import { getRandomPort } from "get-port-please";
|
|
2
|
-
import { describe, expect, it } from "vitest";
|
|
3
|
-
|
|
4
|
-
import { FastMCP } from "./FastMCP.js";
|
|
5
|
-
|
|
6
|
-
describe("FastMCP OAuth Support", () => {
|
|
7
|
-
it("should serve OAuth authorization server metadata", async () => {
|
|
8
|
-
const port = await getRandomPort();
|
|
9
|
-
|
|
10
|
-
const server = new FastMCP({
|
|
11
|
-
name: "Test Server",
|
|
12
|
-
oauth: {
|
|
13
|
-
authorizationServer: {
|
|
14
|
-
authorizationEndpoint: "https://auth.example.com/oauth/authorize",
|
|
15
|
-
dpopSigningAlgValuesSupported: ["ES256", "RS256"],
|
|
16
|
-
grantTypesSupported: ["authorization_code", "refresh_token"],
|
|
17
|
-
issuer: "https://auth.example.com",
|
|
18
|
-
jwksUri: "https://auth.example.com/.well-known/jwks.json",
|
|
19
|
-
responseTypesSupported: ["code"],
|
|
20
|
-
scopesSupported: ["read", "write"],
|
|
21
|
-
tokenEndpoint: "https://auth.example.com/oauth/token",
|
|
22
|
-
},
|
|
23
|
-
enabled: true,
|
|
24
|
-
},
|
|
25
|
-
version: "1.0.0",
|
|
26
|
-
});
|
|
27
|
-
|
|
28
|
-
await server.start({
|
|
29
|
-
httpStream: { port },
|
|
30
|
-
transportType: "httpStream",
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
try {
|
|
34
|
-
// Test the OAuth authorization server endpoint
|
|
35
|
-
const response = await fetch(
|
|
36
|
-
`http://localhost:${port}/.well-known/oauth-authorization-server`,
|
|
37
|
-
);
|
|
38
|
-
expect(response.status).toBe(200);
|
|
39
|
-
expect(response.headers.get("content-type")).toBe("application/json");
|
|
40
|
-
|
|
41
|
-
const metadata = (await response.json()) as Record<string, unknown>;
|
|
42
|
-
|
|
43
|
-
// Check that camelCase was converted to snake_case
|
|
44
|
-
expect(metadata.issuer).toBe("https://auth.example.com");
|
|
45
|
-
expect(metadata.authorization_endpoint).toBe(
|
|
46
|
-
"https://auth.example.com/oauth/authorize",
|
|
47
|
-
);
|
|
48
|
-
expect(metadata.token_endpoint).toBe(
|
|
49
|
-
"https://auth.example.com/oauth/token",
|
|
50
|
-
);
|
|
51
|
-
expect(metadata.response_types_supported).toEqual(["code"]);
|
|
52
|
-
expect(metadata.jwks_uri).toBe(
|
|
53
|
-
"https://auth.example.com/.well-known/jwks.json",
|
|
54
|
-
);
|
|
55
|
-
expect(metadata.scopes_supported).toEqual(["read", "write"]);
|
|
56
|
-
expect(metadata.grant_types_supported).toEqual([
|
|
57
|
-
"authorization_code",
|
|
58
|
-
"refresh_token",
|
|
59
|
-
]);
|
|
60
|
-
expect(metadata.dpop_signing_alg_values_supported).toEqual([
|
|
61
|
-
"ES256",
|
|
62
|
-
"RS256",
|
|
63
|
-
]);
|
|
64
|
-
} finally {
|
|
65
|
-
await server.stop();
|
|
66
|
-
}
|
|
67
|
-
});
|
|
68
|
-
|
|
69
|
-
it("should serve OAuth protected resource metadata", async () => {
|
|
70
|
-
const port = await getRandomPort();
|
|
71
|
-
|
|
72
|
-
const server = new FastMCP({
|
|
73
|
-
name: "Test Server",
|
|
74
|
-
oauth: {
|
|
75
|
-
enabled: true,
|
|
76
|
-
protectedResource: {
|
|
77
|
-
authorizationDetailsTypesSupported: ["payment_initiation"],
|
|
78
|
-
authorizationServers: ["https://auth.example.com"],
|
|
79
|
-
bearerMethodsSupported: ["header"],
|
|
80
|
-
dpopBoundAccessTokensRequired: true,
|
|
81
|
-
dpopSigningAlgValuesSupported: ["ES256", "RS256"],
|
|
82
|
-
jwksUri: "https://test-server.example.com/.well-known/jwks.json",
|
|
83
|
-
resource: "mcp://test-server",
|
|
84
|
-
resourceDocumentation: "https://docs.example.com/api",
|
|
85
|
-
resourceName: "Test API",
|
|
86
|
-
resourcePolicyUri: "https://test-server.example.com/policy",
|
|
87
|
-
resourceSigningAlgValuesSupported: ["RS256"],
|
|
88
|
-
resourceTosUri: "https://test-server.example.com/tos",
|
|
89
|
-
scopesSupported: ["read", "write", "admin"],
|
|
90
|
-
serviceDocumentation: "https://developer.example.com/api",
|
|
91
|
-
tlsClientCertificateBoundAccessTokens: false,
|
|
92
|
-
vendorPrefix_complexObject: {
|
|
93
|
-
nestedArray: [1, 2, 3],
|
|
94
|
-
nestedProperty: "nested value",
|
|
95
|
-
},
|
|
96
|
-
// Vendor extensions (dynamic properties)
|
|
97
|
-
vendorPrefix_customField: "custom value",
|
|
98
|
-
x_api_version: "2.0",
|
|
99
|
-
},
|
|
100
|
-
},
|
|
101
|
-
version: "1.0.0",
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
await server.start({
|
|
105
|
-
httpStream: { port },
|
|
106
|
-
transportType: "httpStream",
|
|
107
|
-
});
|
|
108
|
-
|
|
109
|
-
try {
|
|
110
|
-
const response = await fetch(
|
|
111
|
-
`http://localhost:${port}/.well-known/oauth-protected-resource`,
|
|
112
|
-
);
|
|
113
|
-
expect(response.status).toBe(200);
|
|
114
|
-
expect(response.headers.get("content-type")).toBe("application/json");
|
|
115
|
-
|
|
116
|
-
const metadata = (await response.json()) as Record<string, unknown>;
|
|
117
|
-
|
|
118
|
-
// Check that camelCase was converted to snake_case
|
|
119
|
-
expect(metadata.resource).toBe("mcp://test-server");
|
|
120
|
-
expect(metadata.authorization_servers).toEqual([
|
|
121
|
-
"https://auth.example.com",
|
|
122
|
-
]);
|
|
123
|
-
expect(metadata.jwks_uri).toBe(
|
|
124
|
-
"https://test-server.example.com/.well-known/jwks.json",
|
|
125
|
-
);
|
|
126
|
-
expect(metadata.bearer_methods_supported).toEqual(["header"]);
|
|
127
|
-
expect(metadata.resource_documentation).toBe(
|
|
128
|
-
"https://docs.example.com/api",
|
|
129
|
-
);
|
|
130
|
-
|
|
131
|
-
// New fields added for RFC 9728 compliance
|
|
132
|
-
expect(metadata.authorization_details_types_supported).toEqual([
|
|
133
|
-
"payment_initiation",
|
|
134
|
-
]);
|
|
135
|
-
expect(metadata.dpop_bound_access_tokens_required).toBe(true);
|
|
136
|
-
expect(metadata.dpop_signing_alg_values_supported).toEqual([
|
|
137
|
-
"ES256",
|
|
138
|
-
"RS256",
|
|
139
|
-
]);
|
|
140
|
-
expect(metadata.resource_name).toBe("Test API");
|
|
141
|
-
expect(metadata.resource_policy_uri).toBe(
|
|
142
|
-
"https://test-server.example.com/policy",
|
|
143
|
-
);
|
|
144
|
-
expect(metadata.resource_signing_alg_values_supported).toEqual(["RS256"]);
|
|
145
|
-
expect(metadata.resource_tos_uri).toBe(
|
|
146
|
-
"https://test-server.example.com/tos",
|
|
147
|
-
);
|
|
148
|
-
expect(metadata.scopes_supported).toEqual(["read", "write", "admin"]);
|
|
149
|
-
expect(metadata.service_documentation).toBe(
|
|
150
|
-
"https://developer.example.com/api",
|
|
151
|
-
);
|
|
152
|
-
expect(metadata.tls_client_certificate_bound_access_tokens).toBe(false);
|
|
153
|
-
|
|
154
|
-
// Vendor extensions (dynamic properties)
|
|
155
|
-
expect(metadata.vendor_prefix_custom_field).toBe("custom value");
|
|
156
|
-
expect(metadata.vendor_prefix_complex_object).toEqual({
|
|
157
|
-
nestedArray: [1, 2, 3],
|
|
158
|
-
nestedProperty: "nested value",
|
|
159
|
-
});
|
|
160
|
-
expect(metadata.x_api_version).toBe("2.0");
|
|
161
|
-
} finally {
|
|
162
|
-
await server.stop();
|
|
163
|
-
}
|
|
164
|
-
});
|
|
165
|
-
|
|
166
|
-
it("should return 404 for OAuth endpoints when disabled", async () => {
|
|
167
|
-
const port = await getRandomPort();
|
|
168
|
-
|
|
169
|
-
const server = new FastMCP({
|
|
170
|
-
name: "Test Server",
|
|
171
|
-
oauth: {
|
|
172
|
-
enabled: false,
|
|
173
|
-
},
|
|
174
|
-
version: "1.0.0",
|
|
175
|
-
});
|
|
176
|
-
|
|
177
|
-
await server.start({
|
|
178
|
-
httpStream: { port },
|
|
179
|
-
transportType: "httpStream",
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
try {
|
|
183
|
-
const authServerResponse = await fetch(
|
|
184
|
-
`http://localhost:${port}/.well-known/oauth-authorization-server`,
|
|
185
|
-
);
|
|
186
|
-
expect(authServerResponse.status).toBe(404);
|
|
187
|
-
|
|
188
|
-
const protectedResourceResponse = await fetch(
|
|
189
|
-
`http://localhost:${port}/.well-known/oauth-protected-resource`,
|
|
190
|
-
);
|
|
191
|
-
expect(protectedResourceResponse.status).toBe(404);
|
|
192
|
-
} finally {
|
|
193
|
-
await server.stop();
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
it("should return 404 for OAuth endpoints when not configured", async () => {
|
|
198
|
-
const port = await getRandomPort();
|
|
199
|
-
|
|
200
|
-
const server = new FastMCP({
|
|
201
|
-
name: "Test Server",
|
|
202
|
-
version: "1.0.0",
|
|
203
|
-
// No oauth configuration
|
|
204
|
-
});
|
|
205
|
-
|
|
206
|
-
await server.start({
|
|
207
|
-
httpStream: { port },
|
|
208
|
-
transportType: "httpStream",
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
try {
|
|
212
|
-
const authServerResponse = await fetch(
|
|
213
|
-
`http://localhost:${port}/.well-known/oauth-authorization-server`,
|
|
214
|
-
);
|
|
215
|
-
expect(authServerResponse.status).toBe(404);
|
|
216
|
-
|
|
217
|
-
const protectedResourceResponse = await fetch(
|
|
218
|
-
`http://localhost:${port}/.well-known/oauth-protected-resource`,
|
|
219
|
-
);
|
|
220
|
-
expect(protectedResourceResponse.status).toBe(404);
|
|
221
|
-
} finally {
|
|
222
|
-
await server.stop();
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it, vi } from "vitest";
|
|
2
|
-
import { z } from "zod";
|
|
3
|
-
|
|
4
|
-
import { FastMCP } from "./FastMCP.js";
|
|
5
|
-
|
|
6
|
-
interface TestAuth {
|
|
7
|
-
[key: string]: unknown; // Required for FastMCPSessionAuth compatibility
|
|
8
|
-
role: "admin" | "user";
|
|
9
|
-
userId: string;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
describe("FastMCP Session Context", () => {
|
|
13
|
-
describe("stdio transport", () => {
|
|
14
|
-
it("should pass session context to tool execution when authenticate is provided", async () => {
|
|
15
|
-
const mockAuth: TestAuth = { role: "admin", userId: "test-user" };
|
|
16
|
-
const server = new FastMCP<TestAuth>({
|
|
17
|
-
authenticate: async (request) => {
|
|
18
|
-
if (!request) return mockAuth;
|
|
19
|
-
|
|
20
|
-
throw new Error("Unexpected request in test");
|
|
21
|
-
},
|
|
22
|
-
name: "test-server",
|
|
23
|
-
version: "1.0.0",
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
server.addTool({
|
|
27
|
-
description: "Test tool to verify session context",
|
|
28
|
-
execute: async (_args, context) => {
|
|
29
|
-
return `Session received: ${context.session ? "yes" : "no"}`;
|
|
30
|
-
},
|
|
31
|
-
name: "test-session-context",
|
|
32
|
-
parameters: z.object({
|
|
33
|
-
message: z.string(),
|
|
34
|
-
}),
|
|
35
|
-
});
|
|
36
|
-
|
|
37
|
-
await server.start({ transportType: "stdio" });
|
|
38
|
-
|
|
39
|
-
expect(server).toBeDefined();
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
it("should handle authentication errors gracefully in stdio transport", async () => {
|
|
43
|
-
const mockLogger = {
|
|
44
|
-
debug: vi.fn(),
|
|
45
|
-
error: vi.fn(),
|
|
46
|
-
info: vi.fn(),
|
|
47
|
-
log: vi.fn(),
|
|
48
|
-
warn: vi.fn(),
|
|
49
|
-
};
|
|
50
|
-
const server = new FastMCP<TestAuth>({
|
|
51
|
-
authenticate: async () => {
|
|
52
|
-
throw new Error("Auth failed");
|
|
53
|
-
},
|
|
54
|
-
logger: mockLogger,
|
|
55
|
-
name: "test-server",
|
|
56
|
-
version: "1.0.0",
|
|
57
|
-
});
|
|
58
|
-
|
|
59
|
-
server.addTool({
|
|
60
|
-
description: "Test tool",
|
|
61
|
-
execute: async (_args, context) => {
|
|
62
|
-
return `Session: ${context.session ? "present" : "undefined"}`;
|
|
63
|
-
},
|
|
64
|
-
name: "test-tool",
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
await server.start({ transportType: "stdio" });
|
|
68
|
-
|
|
69
|
-
expect(mockLogger.error).toHaveBeenCalledWith(
|
|
70
|
-
"[FastMCP error] Authentication failed for stdio transport:",
|
|
71
|
-
"Auth failed",
|
|
72
|
-
);
|
|
73
|
-
});
|
|
74
|
-
|
|
75
|
-
it("should work without authenticate function", async () => {
|
|
76
|
-
const server = new FastMCP({
|
|
77
|
-
name: "test-server",
|
|
78
|
-
version: "1.0.0",
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
server.addTool({
|
|
82
|
-
description: "Test tool without auth",
|
|
83
|
-
execute: async (_args, context) => {
|
|
84
|
-
return `Session: ${context.session ? "present" : "undefined"}`;
|
|
85
|
-
},
|
|
86
|
-
name: "test-tool",
|
|
87
|
-
});
|
|
88
|
-
|
|
89
|
-
await server.start({ transportType: "stdio" });
|
|
90
|
-
|
|
91
|
-
expect(server).toBeDefined();
|
|
92
|
-
});
|
|
93
|
-
});
|
|
94
|
-
|
|
95
|
-
describe("environment variable based authentication", () => {
|
|
96
|
-
it("should support reading from environment variables in stdio mode", async () => {
|
|
97
|
-
const originalEnv = process.env.TEST_USER_ID;
|
|
98
|
-
|
|
99
|
-
process.env.TEST_USER_ID = "env-user-123";
|
|
100
|
-
|
|
101
|
-
try {
|
|
102
|
-
const server = new FastMCP<TestAuth>({
|
|
103
|
-
authenticate: async (request) => {
|
|
104
|
-
if (!request) {
|
|
105
|
-
return {
|
|
106
|
-
role: "user" as const,
|
|
107
|
-
userId: process.env.TEST_USER_ID || "default-user",
|
|
108
|
-
};
|
|
109
|
-
}
|
|
110
|
-
throw new Error("HTTP not supported in this test");
|
|
111
|
-
},
|
|
112
|
-
name: "test-server",
|
|
113
|
-
version: "1.0.0",
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
server.addTool({
|
|
117
|
-
description: "Tool using env-based auth",
|
|
118
|
-
execute: async (_args, context) => {
|
|
119
|
-
return `Environment user: ${context.session?.userId}`;
|
|
120
|
-
},
|
|
121
|
-
name: "env-test-tool",
|
|
122
|
-
});
|
|
123
|
-
|
|
124
|
-
await server.start({ transportType: "stdio" });
|
|
125
|
-
|
|
126
|
-
expect(server).toBeDefined();
|
|
127
|
-
} finally {
|
|
128
|
-
if (originalEnv !== undefined) {
|
|
129
|
-
process.env.TEST_USER_ID = originalEnv;
|
|
130
|
-
} else {
|
|
131
|
-
delete process.env.TEST_USER_ID;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
});
|
|
136
|
-
});
|