fa-mcp-sdk 0.4.141 → 0.11.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/README.md +5 -0
- package/cli-template/.dockerignore +16 -0
- package/cli-template/.gitlab-ci.yml +135 -0
- package/cli-template/AGENTS.md +1 -0
- package/cli-template/CHANGELOG.md +64 -0
- package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +27 -4
- package/cli-template/FA-MCP-SDK-DOC/02-1-tools-and-api.md +195 -0
- package/cli-template/FA-MCP-SDK-DOC/02-2-prompts-and-resources.md +172 -9
- package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +170 -12
- package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +158 -8
- package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +67 -6
- package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +31 -15
- package/cli-template/FA-MCP-SDK-DOC/10-mcp-apps.md +1 -1
- package/cli-template/FA-MCP-SDK-DOC/11-public-contract.md +342 -0
- package/cli-template/README.md +37 -0
- package/cli-template/deploy/docker/.env.example +10 -0
- package/cli-template/deploy/docker/Dockerfile +44 -0
- package/cli-template/deploy/docker/Dockerfile.local +29 -0
- package/cli-template/deploy/docker/README.md +94 -0
- package/cli-template/deploy/docker/config/local.docker.yaml +14 -0
- package/cli-template/deploy/docker/docker-compose.yml +31 -0
- package/cli-template/deploy/gitlab-runner/.env.example +16 -0
- package/cli-template/deploy/gitlab-runner/README.md +65 -0
- package/cli-template/deploy/gitlab-runner/config/config.toml.template +26 -0
- package/cli-template/deploy/gitlab-runner/docker-compose.yml +39 -0
- package/cli-template/deploy/gitlab-runner/entrypoint.sh +27 -0
- package/cli-template/deploy/gitlab-runner/start.sh +47 -0
- package/cli-template/gitignore +96 -95
- package/cli-template/package.json +1 -1
- package/config/_local.yaml +73 -11
- package/config/custom-environment-variables.yaml +102 -0
- package/config/default.yaml +164 -11
- package/config/local.yaml +20 -19
- package/dist/core/_types_/config.d.ts +119 -0
- package/dist/core/_types_/config.d.ts.map +1 -1
- package/dist/core/_types_/types.d.ts +137 -4
- package/dist/core/_types_/types.d.ts.map +1 -1
- package/dist/core/agent-tester/agent-tester-router.d.ts.map +1 -1
- package/dist/core/agent-tester/agent-tester-router.js +25 -11
- package/dist/core/agent-tester/agent-tester-router.js.map +1 -1
- package/dist/core/agent-tester/services/TesterMcpClientService.d.ts.map +1 -1
- package/dist/core/agent-tester/services/TesterMcpClientService.js +6 -4
- package/dist/core/agent-tester/services/TesterMcpClientService.js.map +1 -1
- package/dist/core/auth/admin-auth.js +4 -4
- package/dist/core/auth/admin-auth.js.map +1 -1
- package/dist/core/auth/agent-tester-auth.d.ts +1 -1
- package/dist/core/auth/agent-tester-auth.d.ts.map +1 -1
- package/dist/core/auth/agent-tester-auth.js +8 -4
- package/dist/core/auth/agent-tester-auth.js.map +1 -1
- package/dist/core/auth/auth-profile.d.ts +38 -0
- package/dist/core/auth/auth-profile.d.ts.map +1 -0
- package/dist/core/auth/auth-profile.js +101 -0
- package/dist/core/auth/auth-profile.js.map +1 -0
- package/dist/core/auth/jwt-v2.d.ts +27 -0
- package/dist/core/auth/jwt-v2.d.ts.map +1 -0
- package/dist/core/auth/jwt-v2.js +180 -0
- package/dist/core/auth/jwt-v2.js.map +1 -0
- package/dist/core/auth/jwt.d.ts +27 -13
- package/dist/core/auth/jwt.d.ts.map +1 -1
- package/dist/core/auth/jwt.js +36 -13
- package/dist/core/auth/jwt.js.map +1 -1
- package/dist/core/auth/key-resolver.d.ts +74 -0
- package/dist/core/auth/key-resolver.d.ts.map +1 -0
- package/dist/core/auth/key-resolver.js +330 -0
- package/dist/core/auth/key-resolver.js.map +1 -0
- package/dist/core/auth/middleware.d.ts.map +1 -1
- package/dist/core/auth/middleware.js +66 -0
- package/dist/core/auth/middleware.js.map +1 -1
- package/dist/core/auth/multi-auth.d.ts +1 -1
- package/dist/core/auth/multi-auth.d.ts.map +1 -1
- package/dist/core/auth/multi-auth.js +7 -7
- package/dist/core/auth/multi-auth.js.map +1 -1
- package/dist/core/auth/token-generator/server.js +4 -4
- package/dist/core/auth/token-generator/server.js.map +1 -1
- package/dist/core/auth/types.d.ts +5 -0
- package/dist/core/auth/types.d.ts.map +1 -1
- package/dist/core/db/pg-db.d.ts +7 -0
- package/dist/core/db/pg-db.d.ts.map +1 -1
- package/dist/core/db/pg-db.js +54 -3
- package/dist/core/db/pg-db.js.map +1 -1
- package/dist/core/errors/BaseMcpError.d.ts +21 -1
- package/dist/core/errors/BaseMcpError.d.ts.map +1 -1
- package/dist/core/errors/BaseMcpError.js +20 -1
- package/dist/core/errors/BaseMcpError.js.map +1 -1
- package/dist/core/errors/ValidationError.d.ts +5 -0
- package/dist/core/errors/ValidationError.d.ts.map +1 -1
- package/dist/core/errors/ValidationError.js +6 -1
- package/dist/core/errors/ValidationError.js.map +1 -1
- package/dist/core/errors/errors.d.ts +31 -3
- package/dist/core/errors/errors.d.ts.map +1 -1
- package/dist/core/errors/errors.js +86 -6
- package/dist/core/errors/errors.js.map +1 -1
- package/dist/core/errors/specific-errors.d.ts +54 -0
- package/dist/core/errors/specific-errors.d.ts.map +1 -0
- package/dist/core/errors/specific-errors.js +82 -0
- package/dist/core/errors/specific-errors.js.map +1 -0
- package/dist/core/index.d.ts +10 -2
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js +9 -1
- package/dist/core/index.js.map +1 -1
- package/dist/core/init-mcp-server.d.ts.map +1 -1
- package/dist/core/init-mcp-server.js +39 -0
- package/dist/core/init-mcp-server.js.map +1 -1
- package/dist/core/mcp/create-mcp-server.d.ts +12 -6
- package/dist/core/mcp/create-mcp-server.d.ts.map +1 -1
- package/dist/core/mcp/create-mcp-server.js +592 -33
- package/dist/core/mcp/create-mcp-server.js.map +1 -1
- package/dist/core/mcp/debug-trace.d.ts +3 -1
- package/dist/core/mcp/debug-trace.d.ts.map +1 -1
- package/dist/core/mcp/debug-trace.js +17 -2
- package/dist/core/mcp/debug-trace.js.map +1 -1
- package/dist/core/mcp/deprecation.d.ts +31 -0
- package/dist/core/mcp/deprecation.d.ts.map +1 -0
- package/dist/core/mcp/deprecation.js +96 -0
- package/dist/core/mcp/deprecation.js.map +1 -0
- package/dist/core/mcp/mcp-logging.d.ts +32 -0
- package/dist/core/mcp/mcp-logging.d.ts.map +1 -0
- package/dist/core/mcp/mcp-logging.js +97 -0
- package/dist/core/mcp/mcp-logging.js.map +1 -0
- package/dist/core/mcp/pagination.d.ts +13 -0
- package/dist/core/mcp/pagination.d.ts.map +1 -0
- package/dist/core/mcp/pagination.js +50 -0
- package/dist/core/mcp/pagination.js.map +1 -0
- package/dist/core/mcp/prompts.d.ts +5 -1
- package/dist/core/mcp/prompts.d.ts.map +1 -1
- package/dist/core/mcp/prompts.js +3 -1
- package/dist/core/mcp/prompts.js.map +1 -1
- package/dist/core/mcp/resources.d.ts +9 -0
- package/dist/core/mcp/resources.d.ts.map +1 -1
- package/dist/core/mcp/resources.js +158 -11
- package/dist/core/mcp/resources.js.map +1 -1
- package/dist/core/mcp/server-stdio.d.ts +7 -1
- package/dist/core/mcp/server-stdio.d.ts.map +1 -1
- package/dist/core/mcp/server-stdio.js +8 -3
- package/dist/core/mcp/server-stdio.js.map +1 -1
- package/dist/core/mcp/task-store.d.ts +97 -0
- package/dist/core/mcp/task-store.d.ts.map +1 -0
- package/dist/core/mcp/task-store.js +175 -0
- package/dist/core/mcp/task-store.js.map +1 -0
- package/dist/core/mcp/tool-limits.d.ts +22 -0
- package/dist/core/mcp/tool-limits.d.ts.map +1 -0
- package/dist/core/mcp/tool-limits.js +115 -0
- package/dist/core/mcp/tool-limits.js.map +1 -0
- package/dist/core/mcp/validate-tool-args.d.ts +16 -0
- package/dist/core/mcp/validate-tool-args.d.ts.map +1 -0
- package/dist/core/mcp/validate-tool-args.js +67 -0
- package/dist/core/mcp/validate-tool-args.js.map +1 -0
- package/dist/core/mcp/validate-tool-names.d.ts +11 -0
- package/dist/core/mcp/validate-tool-names.d.ts.map +1 -0
- package/dist/core/mcp/validate-tool-names.js +23 -0
- package/dist/core/mcp/validate-tool-names.js.map +1 -0
- package/dist/core/metrics/metrics.d.ts +45 -0
- package/dist/core/metrics/metrics.d.ts.map +1 -0
- package/dist/core/metrics/metrics.js +119 -0
- package/dist/core/metrics/metrics.js.map +1 -0
- package/dist/core/utils/mask-sensitive.d.ts +44 -0
- package/dist/core/utils/mask-sensitive.d.ts.map +1 -0
- package/dist/core/utils/mask-sensitive.js +64 -0
- package/dist/core/utils/mask-sensitive.js.map +1 -0
- package/dist/core/utils/testing/McpHttpClient.d.ts +8 -33
- package/dist/core/utils/testing/McpHttpClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpHttpClient.js +8 -74
- package/dist/core/utils/testing/McpHttpClient.js.map +1 -1
- package/dist/core/utils/testing/McpStreamableHttpClient.d.ts +24 -30
- package/dist/core/utils/testing/McpStreamableHttpClient.d.ts.map +1 -1
- package/dist/core/utils/testing/McpStreamableHttpClient.js +36 -198
- package/dist/core/utils/testing/McpStreamableHttpClient.js.map +1 -1
- package/dist/core/utils/utils.d.ts.map +1 -1
- package/dist/core/utils/utils.js +2 -0
- package/dist/core/utils/utils.js.map +1 -1
- package/dist/core/web/admin-router.js +3 -3
- package/dist/core/web/admin-router.js.map +1 -1
- package/dist/core/web/cors.d.ts +9 -1
- package/dist/core/web/cors.d.ts.map +1 -1
- package/dist/core/web/cors.js +26 -5
- package/dist/core/web/cors.js.map +1 -1
- package/dist/core/web/event-store.d.ts +33 -0
- package/dist/core/web/event-store.d.ts.map +1 -0
- package/dist/core/web/event-store.js +65 -0
- package/dist/core/web/event-store.js.map +1 -0
- package/dist/core/web/oauth-router.d.ts +37 -0
- package/dist/core/web/oauth-router.d.ts.map +1 -0
- package/dist/core/web/oauth-router.js +207 -0
- package/dist/core/web/oauth-router.js.map +1 -0
- package/dist/core/web/request-id.d.ts +44 -0
- package/dist/core/web/request-id.d.ts.map +1 -0
- package/dist/core/web/request-id.js +82 -0
- package/dist/core/web/request-id.js.map +1 -0
- package/dist/core/web/server-http.d.ts.map +1 -1
- package/dist/core/web/server-http.js +322 -182
- package/dist/core/web/server-http.js.map +1 -1
- package/package.json +15 -2
- package/scripts/claude-2-agents-symlink.js +10 -1
- package/scripts/generate-jwt.js +129 -51
- package/src/template/custom-resources.ts +14 -0
- package/src/template/prompts/custom-prompts.ts +4 -0
- package/src/template/tools/handle-tool-call.ts +59 -3
- package/src/template/tools/tools.ts +92 -31
- package/src/tests/mcp/test-http.js +1 -1
- package/src/tests/mcp/test-sse.js +1 -1
|
@@ -3,27 +3,53 @@
|
|
|
3
3
|
## Error Classes
|
|
4
4
|
|
|
5
5
|
```typescript
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
BaseMcpError, ToolExecutionError, ValidationError, ServerError,
|
|
8
|
+
// Phase 1 HTTP hardening — Appendix B specific errors:
|
|
9
|
+
PayloadTooLargeError, TimeoutError, RateLimitedError, ResourceNotFoundError,
|
|
10
|
+
MCP_ERROR_CODES, IMcpErrorData,
|
|
11
|
+
} from 'fa-mcp-sdk';
|
|
7
12
|
|
|
8
13
|
throw new ValidationError('Input validation failed');
|
|
9
14
|
throw new ToolExecutionError('my_tool', 'Execution failed');
|
|
10
15
|
throw new ServerError('Database connection failed', { key: 'value' });
|
|
11
16
|
|
|
12
|
-
//
|
|
17
|
+
// Standard §13 / Appendix B — already used by the SDK transport, but exported for tool / API code:
|
|
18
|
+
throw new PayloadTooLargeError('Image exceeds 1 MiB'); // -32005 / 413
|
|
19
|
+
throw new TimeoutError('Upstream did not respond in time'); // -32004 / 504
|
|
20
|
+
throw new RateLimitedError('Too many calls', 30); // -32003 / 429 (retryAfter=30s)
|
|
21
|
+
throw new ResourceNotFoundError('Ticket not found', { field: 'key' }); // -32002 / 404
|
|
22
|
+
|
|
23
|
+
// Custom error — supply both the legacy string code and the JSON-RPC numeric code
|
|
13
24
|
class MyError extends BaseMcpError {
|
|
14
|
-
constructor(msg: string) {
|
|
25
|
+
constructor(msg: string) {
|
|
26
|
+
super('MY_ERROR', msg, undefined, 422, undefined, -32600, { reason: 'my_reason' });
|
|
27
|
+
}
|
|
15
28
|
}
|
|
16
29
|
```
|
|
17
30
|
|
|
18
|
-
|
|
31
|
+
| Class | `code` | `jsonRpcCode` | HTTP | Standard |
|
|
32
|
+
|-------|--------|---------------|------|----------|
|
|
33
|
+
| `ServerError` | `SERVER_ERROR` | `-32000` (default) | 500 | — |
|
|
34
|
+
| `ToolExecutionError` | `TOOL_EXECUTION_ERROR` | `-32000` (default) | 400 | — |
|
|
35
|
+
| `ValidationError` | `VALIDATION_ERROR` | `-32000` (default) | 400 | — |
|
|
36
|
+
| `ResourceNotFoundError` | `RESOURCE_NOT_FOUND` | `-32002` | 404 | Appendix B |
|
|
37
|
+
| `RateLimitedError` | `RATE_LIMITED` | `-32003` | 429 | Appendix B |
|
|
38
|
+
| `TimeoutError` | `TIMEOUT` | `-32004` | 504 | Appendix B |
|
|
39
|
+
| `PayloadTooLargeError` | `PAYLOAD_TOO_LARGE` | `-32005` | 413 | Appendix B |
|
|
19
40
|
|
|
20
41
|
## Error Utilities
|
|
21
42
|
|
|
22
43
|
```typescript
|
|
23
44
|
import { createJsonRpcErrorResponse, toError, toStr, addErrorMessage } from 'fa-mcp-sdk';
|
|
24
45
|
|
|
25
|
-
// Create JSON-RPC error response
|
|
26
|
-
|
|
46
|
+
// Create JSON-RPC error response. Optional third argument injects extra `error.data` keys
|
|
47
|
+
// (standard Appendix B.3 — { requestId?, field?, reason?, retryAfter?, … }).
|
|
48
|
+
const response = createJsonRpcErrorResponse(error, 'request-123', { requestId: 'req-abc' });
|
|
49
|
+
|
|
50
|
+
// Resulting body:
|
|
51
|
+
// { jsonrpc: '2.0', id: 'request-123',
|
|
52
|
+
// error: { code: -32004, message: '…', data: { reason: 'tool_timeout', requestId: 'req-abc' } } }
|
|
27
53
|
|
|
28
54
|
// Safe error conversion
|
|
29
55
|
const err = toError(anything); // → Error object
|
|
@@ -124,6 +150,41 @@ function getJsonFromResult<T = any>(result: TToolHandlerResponse | any): T;
|
|
|
124
150
|
ignore `tools.answerAs`. Use when a specific shape is required.
|
|
125
151
|
- **`getJsonFromResult()`** — Inverse of `formatToolResult()`. Extracts JSON from either format. Use in tests.
|
|
126
152
|
|
|
153
|
+
## Masking Sensitive Data (`maskSensitive`, standard §12.2)
|
|
154
|
+
|
|
155
|
+
Masking personal / sensitive data in tool results is the **server's** responsibility — the SDK never
|
|
156
|
+
masks automatically (it does not know the domain model). `maskSensitive(value, rules)` is an optional,
|
|
157
|
+
reusable helper: call it inside a tool handler before returning the result.
|
|
158
|
+
|
|
159
|
+
```typescript
|
|
160
|
+
import { maskSensitive } from 'fa-mcp-sdk';
|
|
161
|
+
|
|
162
|
+
const result = await fetchUserRecord(args);
|
|
163
|
+
// Mask by field name (case-insensitive) and/or regex on string values at any depth.
|
|
164
|
+
const safe = maskSensitive(result, {
|
|
165
|
+
fieldNames: ['password', 'token', 'ssn', 'emailAddress'],
|
|
166
|
+
patterns: [/\b\d{13,19}\b/g], // card-like number sequences
|
|
167
|
+
replacement: '***', // default; or a function for partial masking
|
|
168
|
+
});
|
|
169
|
+
return formatToolResult(safe);
|
|
170
|
+
|
|
171
|
+
// Partial masking via a replacement function:
|
|
172
|
+
maskSensitive({ card: '4111111111111111' }, {
|
|
173
|
+
fieldNames: ['card'],
|
|
174
|
+
replacement: (v) => `${v.slice(0, 4)}********${v.slice(-4)}`, // → '4111********1111'
|
|
175
|
+
});
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
`IMaskRules`: `fieldNames?: string[]` (whole value of a matching key is replaced), `patterns?: RegExp[]`
|
|
179
|
+
(matches in string values are replaced; use a global flag to replace all in one string), `replacement?:
|
|
180
|
+
string | ((original: string) => string)` (default `'***'`). The input is never mutated; primitives pass
|
|
181
|
+
through unchanged. It is **not** wired into `tools/call` — applying it and choosing the rules stays with
|
|
182
|
+
the server.
|
|
183
|
+
|
|
184
|
+
> **Caveat.** Mask what genuinely must not leave the server (secrets, raw emails, account ids). Avoid
|
|
185
|
+
> blanket-masking display names the assistant actually needs to be useful (e.g. an issue's assignee) —
|
|
186
|
+
> that trades correctness for nothing. Pick field names deliberately.
|
|
187
|
+
|
|
127
188
|
## Network Utilities
|
|
128
189
|
|
|
129
190
|
```typescript
|
|
@@ -20,13 +20,20 @@ const prompt = await client.getPrompt('agent_brief');
|
|
|
20
20
|
|
|
21
21
|
### HTTP Transport
|
|
22
22
|
|
|
23
|
+
> **Deprecated:** `McpHttpClient` is now a thin alias of `McpStreamableHttpClient` (see below). The
|
|
24
|
+
> server's `/mcp` endpoint runs the SDK `StreamableHTTPServerTransport`, so the old plain-POST client
|
|
25
|
+
> no longer applies — both names speak the same Streamable HTTP protocol. Prefer
|
|
26
|
+
> `McpStreamableHttpClient` in new code.
|
|
27
|
+
|
|
23
28
|
```typescript
|
|
24
29
|
import { McpHttpClient } from 'fa-mcp-sdk';
|
|
25
30
|
|
|
26
|
-
|
|
27
|
-
const
|
|
28
|
-
|
|
31
|
+
// Auth headers go in the constructor (not as a per-call argument).
|
|
32
|
+
const client = new McpHttpClient('http://localhost:3000', {
|
|
33
|
+
headers: { Authorization: 'Bearer token' },
|
|
29
34
|
});
|
|
35
|
+
await client.initialize();
|
|
36
|
+
const result = await client.callTool('my_tool', { query: 'test' });
|
|
30
37
|
```
|
|
31
38
|
|
|
32
39
|
### SSE Transport
|
|
@@ -38,41 +45,50 @@ const client = new McpSseClient('http://localhost:3000');
|
|
|
38
45
|
const result = await client.callTool('my_tool', { query: 'test' });
|
|
39
46
|
```
|
|
40
47
|
|
|
41
|
-
### Streamable HTTP (MCP 2025)
|
|
48
|
+
### Streamable HTTP (MCP 2025-11-25)
|
|
49
|
+
|
|
50
|
+
Wraps the official `@modelcontextprotocol/sdk` `Client` + `StreamableHTTPClientTransport`. The SDK
|
|
51
|
+
transport sets `Accept: application/json, text/event-stream`, captures/resends `Mcp-Session-Id`,
|
|
52
|
+
negotiates the protocol version, and sends `DELETE /mcp` on `close()`.
|
|
42
53
|
|
|
43
54
|
```typescript
|
|
44
55
|
import { McpStreamableHttpClient } from 'fa-mcp-sdk';
|
|
45
56
|
|
|
46
57
|
const client = new McpStreamableHttpClient('http://localhost:3000', {
|
|
47
|
-
headers: {
|
|
58
|
+
headers: { Authorization: 'Bearer token' },
|
|
48
59
|
requestTimeoutMs: 60000,
|
|
49
60
|
});
|
|
50
61
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
clientInfo: { name: 'test-client', version: '1.0.0' },
|
|
54
|
-
});
|
|
62
|
+
// `initialize()` runs the full handshake; the server answers the negotiated version (2025-11-25).
|
|
63
|
+
await client.initialize();
|
|
55
64
|
|
|
56
65
|
const result = await client.callTool('my_tool', { query: 'test' });
|
|
57
66
|
const prompt = await client.getPrompt('agent_brief');
|
|
58
67
|
const resources = await client.listResources();
|
|
59
68
|
const content = await client.readResource('custom://data');
|
|
60
69
|
|
|
61
|
-
// Notifications
|
|
62
|
-
const unsub = client.onNotification('notifications/tools/list_changed', (p) => console.log(p));
|
|
63
|
-
|
|
64
70
|
await client.close();
|
|
65
71
|
```
|
|
66
72
|
|
|
67
|
-
**Methods:** `initialize`, `close`, `callTool`, `getPrompt`, `listResources`, `readResource`,
|
|
73
|
+
**Methods:** `initialize`, `close`, `callTool`, `getPrompt`, `listResources`, `readResource`,
|
|
74
|
+
`listTools`, `listPrompts`. After `initialize()`, `client.serverInfo`, `client.capabilities` and
|
|
75
|
+
`client.protocolVersion` are populated.
|
|
68
76
|
|
|
69
77
|
## Transport Types
|
|
70
78
|
|
|
71
79
|
| Transport | Config | Use Case |
|
|
72
80
|
|-----------|--------|----------|
|
|
73
81
|
| STDIO | `mcp.transportType: "stdio"` | CLI, local dev |
|
|
74
|
-
| HTTP | `mcp.transportType: "http"` | Web integrations, REST API |
|
|
75
|
-
| SSE | HTTP transport | Long-running ops, streaming |
|
|
82
|
+
| HTTP (Streamable HTTP) | `mcp.transportType: "http"` | Web integrations, REST API |
|
|
83
|
+
| SSE (legacy) | HTTP transport | Long-running ops, streaming; older clients |
|
|
84
|
+
|
|
85
|
+
The HTTP transport is **Streamable HTTP** (SDK `StreamableHTTPServerTransport`), stateful per session:
|
|
86
|
+
|
|
87
|
+
| Method `/mcp` | Behaviour |
|
|
88
|
+
|---------------|-----------|
|
|
89
|
+
| `POST` | `initialize` opens a session (server returns `Mcp-Session-Id`); subsequent calls must echo it. Notifications → **202**. No session + not `initialize` → **400**. |
|
|
90
|
+
| `GET` | Server→client SSE stream for the session. |
|
|
91
|
+
| `DELETE` | Tears down the session. |
|
|
76
92
|
|
|
77
93
|
## Running Tests
|
|
78
94
|
|
|
@@ -485,7 +485,7 @@ import {
|
|
|
485
485
|
|-----------|--------|--------------|
|
|
486
486
|
| STDIO | `Server.getClientCapabilities()` (low-level SDK) read inside every handler in `createMcpServer()`. | Every call. |
|
|
487
487
|
| SSE | Same as STDIO, but resolved per-connection (each SSE session owns its own `Server`). | Every call. |
|
|
488
|
-
| Streamable HTTP |
|
|
488
|
+
| Streamable HTTP | Same as STDIO — each session owns its own `Server` + `StreamableHTTPServerTransport` (stateful, keyed by `Mcp-Session-Id`, in-memory `Map` with soft FIFO cap of 4096 sessions). `getClientCapabilities()` is read per handler. | Every call within a session. A client that never sends `Mcp-Session-Id` cannot make post-`initialize` calls (→ 400), so handlers still treat `undefined` as text-only. |
|
|
489
489
|
|
|
490
490
|
```ts
|
|
491
491
|
export const handleToolCall = async (params: IToolHandlerParams) => {
|
|
@@ -0,0 +1,342 @@
|
|
|
1
|
+
# 11 — Public Contract
|
|
2
|
+
|
|
3
|
+
This document is the **formal public contract** of the `fa-mcp-sdk` package. Everything listed
|
|
4
|
+
here is part of the API surface that the SDK guarantees, and every change to it follows the
|
|
5
|
+
versioning policy at the bottom of this file (semver: MAJOR / MINOR / PATCH).
|
|
6
|
+
|
|
7
|
+
If a behaviour is **not** described here — even if it is currently observable in the source — it
|
|
8
|
+
is considered an implementation detail and may change in any release.
|
|
9
|
+
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
## 1. Transports
|
|
13
|
+
|
|
14
|
+
| Transport | Standard | Status | Notes |
|
|
15
|
+
|--------------------|----------|--------|-------------------------------------------------------------|
|
|
16
|
+
| `stdio` | §6 | MUST | Single JSON-RPC stream over stdin/stdout |
|
|
17
|
+
| `streamable_http` | §6 | MUST | `POST/GET/DELETE /mcp` driven by the SDK transport |
|
|
18
|
+
| `legacy_http_sse` | §6 | SHOULD | `GET /sse` + `POST /messages` — kept for backwards-compat |
|
|
19
|
+
|
|
20
|
+
All HTTP routes hosted by the SDK are listed in §2.
|
|
21
|
+
|
|
22
|
+
**SSE resumability (opt-in, §6 MAY).** With `mcp.sse.resumability: true` the Streamable HTTP transport
|
|
23
|
+
keeps recent SSE events in a per-process in-memory ring buffer (`mcp.sse.maxStoredEvents`, default 1000),
|
|
24
|
+
so a client reconnecting to `GET /mcp` with a `Last-Event-ID` header replays the events it missed. Off by
|
|
25
|
+
default. The buffer does not survive a restart and does not span multiple server instances — a persistent
|
|
26
|
+
store would be required for that.
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## 2. HTTP endpoints
|
|
31
|
+
|
|
32
|
+
| Path | Method | Auth | Level | Purpose |
|
|
33
|
+
|---------------------------------------------------|--------|------|--------|---------------------------------------------------------------|
|
|
34
|
+
| `/mcp` | POST | Yes | MUST | JSON-RPC entry point (`initialize`, `tools/*`, …) |
|
|
35
|
+
| `/mcp` | GET | Yes | MUST | Server-initiated SSE stream for the active session |
|
|
36
|
+
| `/mcp` | DELETE | Yes | MUST | Session teardown |
|
|
37
|
+
| `/sse` | GET | Yes | SHOULD | Legacy SSE connect |
|
|
38
|
+
| `/sse` | POST | Yes | SHOULD | Legacy direct JSON-RPC |
|
|
39
|
+
| `/messages` | POST | Yes | SHOULD | Legacy SSE message channel |
|
|
40
|
+
| `/health` | GET | No | MUST | Liveness; returns `{status, version, uptime, details}` |
|
|
41
|
+
| `/ready` | GET | No | SHOULD | Readiness; `{status, checks}` |
|
|
42
|
+
| `/metrics` | GET | No | SHOULD | Prometheus exposition (opt-in via `webServer.metrics.enabled`) |
|
|
43
|
+
| `/` | GET | No | MAY | Static home page |
|
|
44
|
+
| `/ct` | POST | No | MUST | Token validity check via JSON body |
|
|
45
|
+
| `/ct?t=…` | GET | No | MAY | Disabled by default (`webServer.tokenCheck.allowQueryToken`) |
|
|
46
|
+
| `/used-http-headers` | GET | No | MAY | Returns the project's `usedHttpHeaders` declaration |
|
|
47
|
+
| `/.well-known/oauth-protected-resource` | GET | No | MUST* | Active in JWT modes `embedded` / `localKey` / `remoteJwks` |
|
|
48
|
+
| `/.well-known/openid-configuration` | GET | No | MUST* | OIDC discovery |
|
|
49
|
+
| `/.well-known/jwks.json` | GET | No | MUST* | JWK Set with the active public key |
|
|
50
|
+
| `/oauth/token` | POST | No | MUST* | Embedded IdP — `grant_type=password` |
|
|
51
|
+
| `/gen-jwt` | POST | Yes | MAY | JWT issuance API (`webServer.genJwtApiEnable`) |
|
|
52
|
+
| `/admin` | GET | Yes | MAY | Token Generator UI |
|
|
53
|
+
| `/agent-tester` | GET | Yes? | MAY | Built-in chat UI (`agentTester.enabled`) |
|
|
54
|
+
| `/api/openapi.json` / `/api/openapi.yaml` / `/docs` | GET | - | MAY | OpenAPI when the project supplies `httpComponents.apiRouter` |
|
|
55
|
+
|
|
56
|
+
`MUST*` rows are mandatory only when the corresponding feature is active.
|
|
57
|
+
|
|
58
|
+
---
|
|
59
|
+
|
|
60
|
+
## 3. Authentication
|
|
61
|
+
|
|
62
|
+
The SDK accepts the following `Authorization` schemes, picked by header format (not order):
|
|
63
|
+
|
|
64
|
+
- `Bearer <token>` — JWT (any of the four modes: `legacyAesCtr` / `embedded` / `localKey` /
|
|
65
|
+
`remoteJwks`) or a permanent server token.
|
|
66
|
+
- `Basic <base64>` — HTTP Basic auth.
|
|
67
|
+
- Optional `customAuthValidator` — last fallback for project-specific schemes.
|
|
68
|
+
|
|
69
|
+
JWT modes are documented in [04-authentication](04-authentication.md). Public contract:
|
|
70
|
+
|
|
71
|
+
| Claim | Required | Notes |
|
|
72
|
+
|-------------|----------|--------------------------------------------------------------------------|
|
|
73
|
+
| `sub` | MUST | Subject — drives rate-limit bucket and concurrency cap |
|
|
74
|
+
| `exp` | MUST | Expiration; SDK enforces with `clockSkew` (default 30 s, max 60 s) |
|
|
75
|
+
| `aud` | SHOULD | Defaults to `appConfig.name`; configurable via `expectedAudience` |
|
|
76
|
+
| `iss` | SHOULD* | Required in modes `embedded` / `localKey` / `remoteJwks` |
|
|
77
|
+
| `scope` | MAY | Space-separated scopes; matched against `requiredScopes` per §7.5 |
|
|
78
|
+
| `ip` | MAY | When set + `isCheckIP=true`, client IP must match |
|
|
79
|
+
| `service` | MAY | When set + `checkMCPName=true`, must contain `appConfig.name` |
|
|
80
|
+
| `jti` | MAY | Used by the revocation list |
|
|
81
|
+
|
|
82
|
+
Scopes are matched against `requiredScopes` on tools, prompts, and resources (§7.5).
|
|
83
|
+
|
|
84
|
+
`WWW-Authenticate: Bearer realm="<name>" resource_metadata="<url>"` is emitted on every 401
|
|
85
|
+
from MCP endpoints per §7.4. 403 responses (authenticated but forbidden) carry NO
|
|
86
|
+
`WWW-Authenticate` header.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 4. MCP methods
|
|
91
|
+
|
|
92
|
+
| Method | Status | Notes |
|
|
93
|
+
|---------------------------------------|--------|-----------------------------------------------------------|
|
|
94
|
+
| `initialize` | MUST | |
|
|
95
|
+
| `notifications/initialized` | MUST | |
|
|
96
|
+
| `tools/list` | MUST | Server-side pagination via `mcp.pagination.pageSize` |
|
|
97
|
+
| `tools/call` | MUST | Honours `signal`, `_meta.progressToken`, `requiredScopes` |
|
|
98
|
+
| `prompts/list` | MUST | Capability advertised only when the server has prompts (§8.2) |
|
|
99
|
+
| `prompts/get` | MUST | Returns `-32601` when no prompts are configured |
|
|
100
|
+
| `resources/list` | MUST | Same pagination contract |
|
|
101
|
+
| `resources/read` | MUST | Returns `text` or base64 `blob` per entry (§11.4) |
|
|
102
|
+
| `resources/templates/list` | MAY | `mcp.resources.templatesEnabled` |
|
|
103
|
+
| `resources/subscribe` / `unsubscribe` | MAY | `mcp.resources.subscribeEnabled` |
|
|
104
|
+
| `completion/complete` | MAY | `mcp.completions.enabled` + `completionProvider` (§8.2) |
|
|
105
|
+
| `tasks/list` | MAY | `mcp.tasks.enabled`; caller's own tasks, newest first, paginated (§8.7) |
|
|
106
|
+
| `tasks/get` | MAY | `mcp.tasks.enabled`; current task metadata (§8.7) |
|
|
107
|
+
| `tasks/result` | MAY | `mcp.tasks.enabled`; the `tools/call` result once completed (§8.7) |
|
|
108
|
+
| `tasks/cancel` | MAY | `mcp.tasks.enabled`; aborts a running task, idempotent (§8.7) |
|
|
109
|
+
| `logging/setLevel` | SHOULD | Capability `logging: {}` (default ON) |
|
|
110
|
+
| `notifications/message` | SHOULD | Emitted by `sendLoggingMessage()` |
|
|
111
|
+
| `notifications/progress` | SHOULD | Emitted by `IToolHandlerParams.sendProgress()` (§8.6) |
|
|
112
|
+
| `notifications/cancelled` | SHOULD | Aborts `IToolHandlerParams.signal` (§8.5) |
|
|
113
|
+
| `notifications/tasks/status` | MAY | Emitted on every task status transition (§8.7) |
|
|
114
|
+
|
|
115
|
+
When `mcp.tasks.enabled` is `true`, the server advertises the `tasks` capability
|
|
116
|
+
(`{ list, cancel, requests: { tools: { call } } }`) and a `tools/call` carrying a `task` parameter
|
|
117
|
+
is executed as a task: the server returns a `CreateTaskResult` (`{ task: { taskId, status, … } }`)
|
|
118
|
+
immediately and runs the tool in the background. A tool opts in via `execution.taskSupport`
|
|
119
|
+
(`optional` / `required` / `forbidden`, see §5) — sending `task` to a tool that does not support it,
|
|
120
|
+
or omitting `task` for a `required` tool, returns `-32602`. The default task store keeps records in
|
|
121
|
+
process memory only; it does **not** survive a restart. When `mcp.tasks.enabled` is `false` (the
|
|
122
|
+
default) the capability is not advertised and all four `tasks/*` methods return `-32601`.
|
|
123
|
+
|
|
124
|
+
---
|
|
125
|
+
|
|
126
|
+
## 5. Tool / Prompt / Resource format
|
|
127
|
+
|
|
128
|
+
### Tool (`Tool` from `@modelcontextprotocol/sdk`)
|
|
129
|
+
|
|
130
|
+
- `name` — MUST be `snake_case` and unique (validated at boot via
|
|
131
|
+
`validate-tool-names.ts`).
|
|
132
|
+
- `description` — MUST be non-empty. Deprecation prefix `[DEPRECATED until …]` is added
|
|
133
|
+
automatically when `_meta.deprecated` is set.
|
|
134
|
+
- `inputSchema` — MUST declare `$schema: 'https://json-schema.org/draft/2020-12/schema'` and
|
|
135
|
+
`additionalProperties: false`.
|
|
136
|
+
- `outputSchema` — MAY; when present, the SDK validates `structuredContent` against it and
|
|
137
|
+
mirrors the value into `content[0]` as JSON text (§12.4).
|
|
138
|
+
- `title` — SHOULD; user-facing label.
|
|
139
|
+
- `execution.taskSupport` — MAY; one of `optional` / `required` / `forbidden` (default — absence is
|
|
140
|
+
treated as `forbidden`, i.e. synchronous only). Controls task-augmented execution (§8.7); passed
|
|
141
|
+
through verbatim in `tools/list`. Effective only when `mcp.tasks.enabled` is `true`.
|
|
142
|
+
- `annotations` — MAY; may be hidden via `mcp.tools.hideAnnotations`.
|
|
143
|
+
- `_meta._meta.requiredScopes` (or top-level `requiredScopes`) — MAY; OAuth scopes enforced
|
|
144
|
+
before dispatch.
|
|
145
|
+
- `_meta.deprecated` — MAY; structured `IDeprecationInfo`.
|
|
146
|
+
- `_meta.ui` — MAY; MCP Apps widget metadata.
|
|
147
|
+
|
|
148
|
+
### Prompt (`IPromptData`)
|
|
149
|
+
|
|
150
|
+
`name`, `description`, `arguments[]` (each `IPromptArgument`), `content` (string or function),
|
|
151
|
+
`requireAuth`, `requiredScopes`, `deprecated`. Optional UI metadata (§10.5, MAY): `title` (human-facing
|
|
152
|
+
label, falls back to `name`) and `icons` (`IIcon[]` — `{ src; mimeType?; sizes? }`). Both pass through
|
|
153
|
+
`prompts/list` unchanged; built-in `agent_brief` / `agent_prompt` carry a `title`.
|
|
154
|
+
|
|
155
|
+
### Resource (`IResourceData` / `IResourceInfo`)
|
|
156
|
+
|
|
157
|
+
`uri`, `name`, `description`, `mimeType`, optional `title`, `size` (bytes, §11.3 MAY),
|
|
158
|
+
`icons` (`IIcon[]`, §11.3 MAY), `requireAuth`, `requiredScopes`, `_meta`, `deprecated`. On
|
|
159
|
+
`resources/list` the SDK computes `size` from the content (UTF-8 byte length for text/objects, buffer
|
|
160
|
+
length for blobs) when the author did not set it; lazy (function) content omits `size`. `content` is a
|
|
161
|
+
string / object / function for text resources, or
|
|
162
|
+
`IResourceBinaryContent` (`{ blob: Buffer | base64-string, base64?: boolean }`) for binary
|
|
163
|
+
resources — `resources/read` then returns base64 `contents[0].blob` (no `text`) with the
|
|
164
|
+
resource's `mimeType` (§11.4 / §12.2). Built-in URI schemes are guaranteed by the SDK:
|
|
165
|
+
|
|
166
|
+
| URI | Purpose |
|
|
167
|
+
|--------------------------------------------|------------------------------------------------|
|
|
168
|
+
| `project://version` | Returns `appConfig.version` |
|
|
169
|
+
| `use://auth` | Authentication self-description |
|
|
170
|
+
| `<service>://agent/brief` | Mirrors `agent_brief` prompt |
|
|
171
|
+
| `<service>://agent/prompt` | Mirrors `agent_prompt` prompt |
|
|
172
|
+
| `doc://...` | Application docs |
|
|
173
|
+
|
|
174
|
+
### Sensitive data masking (`maskSensitive`, §12.2)
|
|
175
|
+
|
|
176
|
+
Masking personal / sensitive data in tool results is the server's responsibility — the SDK never masks
|
|
177
|
+
automatically. The optional helper `maskSensitive(value, rules)` (exported from the barrel, with the
|
|
178
|
+
`IMaskRules` type) is a reusable building block: it walks an object / array / string and applies explicit
|
|
179
|
+
rules — `fieldNames` (case-insensitive field-name match) and `patterns` (regular expressions on string
|
|
180
|
+
values at any depth) — replacing matches with `replacement` (a string, default `'***'`, or a function for
|
|
181
|
+
partial masking like `4111********1111`). It returns a new value and never mutates the input. Call it
|
|
182
|
+
inside a tool handler before returning the result; choosing the rules and where to apply them stays with
|
|
183
|
+
the server.
|
|
184
|
+
|
|
185
|
+
---
|
|
186
|
+
|
|
187
|
+
## 6. Error format
|
|
188
|
+
|
|
189
|
+
JSON-RPC errors follow Appendix B of the standard. Mapping (JSON-RPC → HTTP):
|
|
190
|
+
|
|
191
|
+
| JSON-RPC code | HTTP | Class | Trigger |
|
|
192
|
+
|---------------|------|----------------------|-----------------------------------------------|
|
|
193
|
+
| `-32600` | 400 | (none) | Invalid Request |
|
|
194
|
+
| `-32601` | 404 | `ResourceNotFoundError` (when applicable) | Method/resource not found |
|
|
195
|
+
| `-32602` | 400 | `ValidationError` | Invalid params (input schema, unknown tool) |
|
|
196
|
+
| `-32603` | 500 | `ServerError` | Internal error |
|
|
197
|
+
| `-32000` | varies | `BaseMcpError` | Generic SDK error |
|
|
198
|
+
| `-32002` | 404 | `ResourceNotFoundError` | Resource lookup failed |
|
|
199
|
+
| `-32003` | 429 | `RateLimitedError` | Rate limit / concurrent-call cap (+ `Retry-After`) |
|
|
200
|
+
| `-32004` | 504 | `TimeoutError` | `mcp.limits.toolTimeoutMs` exceeded |
|
|
201
|
+
| `-32005` | 413 | `PayloadTooLargeError` | `mcp.limits.maxPayloadBytes` exceeded |
|
|
202
|
+
| `-32006` | 503 | `UpstreamUnavailableError` | Dependency (DB / downstream) unreachable |
|
|
203
|
+
| `-32007` | 409 | `ConflictError` | State conflict (duplicate / optimistic lock) |
|
|
204
|
+
|
|
205
|
+
Unrecognized internal errors are sanitized (§13.3 / Appendix C.3): the outward `error.message`
|
|
206
|
+
collapses to `Internal error`, the full text is written to the internal log keyed by `requestId`,
|
|
207
|
+
and absolute filesystem paths are scrubbed from any outward message. Recognized domain errors (any
|
|
208
|
+
class above) keep their message verbatim.
|
|
209
|
+
|
|
210
|
+
`error.data` is structured per Appendix B.3:
|
|
211
|
+
|
|
212
|
+
```jsonc
|
|
213
|
+
{
|
|
214
|
+
"requestId": "uuid…", // §15.1, always set by the SDK if absent
|
|
215
|
+
"field": "name", // input validation diagnostics
|
|
216
|
+
"reason": "invalid_type", // machine-readable hint
|
|
217
|
+
"retryAfter": 12 // seconds, for -32003
|
|
218
|
+
// …implementation-specific keys are allowed but not part of the contract
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
---
|
|
223
|
+
|
|
224
|
+
## 7. Limits and headers
|
|
225
|
+
|
|
226
|
+
| Limit / Header | Source / Default |
|
|
227
|
+
|------------------------------|-------------------------------------------------------------------------|
|
|
228
|
+
| `mcp.limits.maxPayloadBytes` | 1 MiB |
|
|
229
|
+
| `mcp.limits.maxToolResultBytes` | 10 MiB |
|
|
230
|
+
| `mcp.limits.toolTimeoutMs` | 30 000 ms |
|
|
231
|
+
| `mcp.rateLimit.maxRequests` | 100 / window |
|
|
232
|
+
| `mcp.rateLimit.windowMs` | 60 000 ms |
|
|
233
|
+
| `mcp.rateLimit.maxConcurrentPerSubject` | 16 |
|
|
234
|
+
| `mcp.pagination.pageSize` | 100 |
|
|
235
|
+
| `mcp.logging.defaultLevel` | `info` (Syslog ladder) |
|
|
236
|
+
| `mcp.progress.throttleMs` | 100 (10 events/s/token) |
|
|
237
|
+
| `mcp.completions.enabled` | `false` (opt-in; needs `completionProvider`) |
|
|
238
|
+
| `mcp.tasks.enabled` | `false` (opt-in; advertises `tasks` capability) |
|
|
239
|
+
| `mcp.tasks.defaultTtlMs` | 3 600 000 ms (finished-task retention; clamped to `[minTtlMs, maxTtlMs]`) |
|
|
240
|
+
| `mcp.tasks.maxTtlMs` | 86 400 000 ms (hard retention ceiling) |
|
|
241
|
+
| `mcp.tasks.pollIntervalMs` | 1000 ms (suggested to client in every task object) |
|
|
242
|
+
| `mcp.tasks.maxTasks` | 1000 (retained tasks; oldest finished evicted first) |
|
|
243
|
+
| `webServer.metrics.enabled` | `false` (opt-in) |
|
|
244
|
+
| `X-Request-Id` (response) | Always present — generated when client did not supply one (§15.1) |
|
|
245
|
+
| `tracestate` (response) | Echoed back unchanged when client supplied a valid value |
|
|
246
|
+
| `WWW-Authenticate` | On every 401 from MCP endpoints (§7.4) |
|
|
247
|
+
| `Retry-After` | On every 429 (§14) |
|
|
248
|
+
| `MCP-Session-Id` | Set by SDK on `initialize`; subsequent requests MUST echo it |
|
|
249
|
+
| `MCP-Protocol-Version` | Negotiated by the SDK transport |
|
|
250
|
+
|
|
251
|
+
---
|
|
252
|
+
|
|
253
|
+
## 8. Versioning policy (§17.1)
|
|
254
|
+
|
|
255
|
+
| Change | Bump |
|
|
256
|
+
|-----------------------------------------------------------------|-------|
|
|
257
|
+
| Removing a tool / prompt / resource | MAJOR |
|
|
258
|
+
| Adding a `required` field to an `inputSchema` | MAJOR |
|
|
259
|
+
| Removing a field from an `outputSchema` | MAJOR |
|
|
260
|
+
| Changing the default JWT algorithm / mode | MAJOR |
|
|
261
|
+
| Renaming or removing an HTTP endpoint | MAJOR |
|
|
262
|
+
| Removing a configuration key (`mcp.*`, `webServer.*`, …) | MAJOR |
|
|
263
|
+
| Backwards-incompatible change to `error.data` shape | MAJOR |
|
|
264
|
+
| Adding a new tool / prompt / resource | MINOR |
|
|
265
|
+
| Adding an optional field to any schema | MINOR |
|
|
266
|
+
| Adding a new capability or behaviour gated by an opt-in flag | MINOR |
|
|
267
|
+
| Adding a new optional configuration key (with safe default) | MINOR |
|
|
268
|
+
| Extending `description` or `title` | PATCH |
|
|
269
|
+
| Bug-fix without changing the contract | PATCH |
|
|
270
|
+
| Documentation-only change | PATCH |
|
|
271
|
+
|
|
272
|
+
`[BREAKING]` is the required marker in `CHANGELOG.md` for any MAJOR entry.
|
|
273
|
+
|
|
274
|
+
### Historical examples
|
|
275
|
+
|
|
276
|
+
| Release | Bump | Driver |
|
|
277
|
+
|---------|-------|------------------------------------------------------------------------|
|
|
278
|
+
| 0.4.145 | MINOR | MCP 2025-11-25 via SDK Streamable HTTP |
|
|
279
|
+
| 0.5.0 | MAJOR | HTTP hardening (default bind `127.0.0.1`, error codes, rate-limit) |
|
|
280
|
+
| 0.6.0 | MAJOR | Tools/Prompts/Resources contract (`additionalProperties:false`, mirror)|
|
|
281
|
+
| 0.7.0 | MAJOR | RS256/ES256 JWT runtime, OAuth/OIDC discovery, scope enforcement |
|
|
282
|
+
| 0.8.x | MINOR | Observability (X-Request-Id, traceparent, logging, metrics, progress) |
|
|
283
|
+
| 0.9.1 | MINOR | Conditional capabilities, `-32006`/`-32007`, binary `blob`, error sanitization, opt-in completions |
|
|
284
|
+
| 0.10.0 | MINOR | Opt-in `tasks` capability (task-augmented execution), `execution.taskSupport`, in-memory task store |
|
|
285
|
+
|
|
286
|
+
---
|
|
287
|
+
|
|
288
|
+
## 9. Deprecation process (§17.2)
|
|
289
|
+
|
|
290
|
+
Authors declare deprecation in a structured shape (no free-form `[DEPRECATED]` in descriptions):
|
|
291
|
+
|
|
292
|
+
```typescript
|
|
293
|
+
// tools.ts
|
|
294
|
+
const myTool: Tool = {
|
|
295
|
+
name: 'old_tool',
|
|
296
|
+
description: 'Returns the rate.',
|
|
297
|
+
_meta: {
|
|
298
|
+
deprecated: { until: '2026-08-28', replacedBy: 'new_tool', note: 'See migration guide' },
|
|
299
|
+
},
|
|
300
|
+
// …
|
|
301
|
+
};
|
|
302
|
+
|
|
303
|
+
// prompts / resources
|
|
304
|
+
const myPrompt: IPromptData = {
|
|
305
|
+
name: 'old_prompt',
|
|
306
|
+
description: '…',
|
|
307
|
+
deprecated: { until: '2026-08-28', replacedBy: 'new_prompt' },
|
|
308
|
+
// …
|
|
309
|
+
};
|
|
310
|
+
```
|
|
311
|
+
|
|
312
|
+
The SDK then:
|
|
313
|
+
|
|
314
|
+
1. mutates `description` on list responses to include
|
|
315
|
+
`[DEPRECATED until YYYY-MM-DD, use <replacedBy>]`;
|
|
316
|
+
2. logs a `logger.warn` the first time per hour each `(kind, name)` is invoked;
|
|
317
|
+
3. logs a `logger.error` at registration time if `until` is already in the past — the entry
|
|
318
|
+
should be removed instead of shipped.
|
|
319
|
+
|
|
320
|
+
**Window**: minimum `2 MINOR releases OR 3 months` from announcement to removal (per §17.2),
|
|
321
|
+
whichever is longer.
|
|
322
|
+
|
|
323
|
+
---
|
|
324
|
+
|
|
325
|
+
## 10. Public contract source list
|
|
326
|
+
|
|
327
|
+
The runtime sources of the contract above are:
|
|
328
|
+
|
|
329
|
+
- `src/core/_types_/types.ts` — `IToolHandlerParams`, `IPromptData`, `IResourceInfo`,
|
|
330
|
+
`IDeprecationInfo`, `McpServerData`.
|
|
331
|
+
- `src/core/_types_/config.ts` — `AppConfig` (every documented configuration key).
|
|
332
|
+
- `src/core/errors/BaseMcpError.ts` + `src/core/errors/specific-errors.ts` — error codes.
|
|
333
|
+
- `src/core/mcp/create-mcp-server.ts` — handler contract.
|
|
334
|
+
- `src/core/mcp/task-store.ts` — `ITaskStore` / `InMemoryTaskStore`, task lifecycle (§8.7).
|
|
335
|
+
- `src/core/web/server-http.ts` — HTTP endpoints, headers, response shape.
|
|
336
|
+
- `src/core/web/request-id.ts` — `X-Request-Id` + W3C trace context middleware.
|
|
337
|
+
- `src/core/mcp/mcp-logging.ts` — `logging` capability.
|
|
338
|
+
- `src/core/metrics/metrics.ts` — Prometheus series.
|
|
339
|
+
- `src/core/mcp/deprecation.ts` — deprecation lifecycle.
|
|
340
|
+
|
|
341
|
+
Anything that lives outside this list (file names, internal helpers, log line formats, etc.) is
|
|
342
|
+
**not** part of the contract and may change without notice.
|
package/cli-template/README.md
CHANGED
|
@@ -208,3 +208,40 @@ Generate an admin-capable JWT:
|
|
|
208
208
|
node scripts/generate-jwt.js -u admin -ttl 30d -p "allow=gen-token"
|
|
209
209
|
```
|
|
210
210
|
|
|
211
|
+
## Public Contract
|
|
212
|
+
|
|
213
|
+
The runtime contract surfaced by this server (transports, HTTP endpoints, JWT claims, tool /
|
|
214
|
+
prompt / resource shape, error mapping, headers, semver and deprecation policy) is documented
|
|
215
|
+
in [FA-MCP-SDK-DOC/11-public-contract.md](FA-MCP-SDK-DOC/11-public-contract.md).
|
|
216
|
+
|
|
217
|
+
Tools, prompts and resources exposed by this project are listed in the table below — update
|
|
218
|
+
this section whenever you add, rename, or remove an entry.
|
|
219
|
+
|
|
220
|
+
### Tools
|
|
221
|
+
|
|
222
|
+
| Name | Description |
|
|
223
|
+
|------|-------------|
|
|
224
|
+
| `<tool_name>` | Short description |
|
|
225
|
+
|
|
226
|
+
### Prompts
|
|
227
|
+
|
|
228
|
+
| Name | Description |
|
|
229
|
+
|------|-------------|
|
|
230
|
+
| `agent_brief` | Short agent description (built-in) |
|
|
231
|
+
| `agent_prompt` | Full system prompt (built-in) |
|
|
232
|
+
|
|
233
|
+
### Resources
|
|
234
|
+
|
|
235
|
+
| URI | Description |
|
|
236
|
+
|-----|-------------|
|
|
237
|
+
| `project://version` | SDK / project version (built-in) |
|
|
238
|
+
| `use://auth` | Authentication self-description (built-in) |
|
|
239
|
+
| `<service>://agent/brief` | Mirror of `agent_brief` (built-in) |
|
|
240
|
+
| `<service>://agent/prompt` | Mirror of `agent_prompt` (built-in) |
|
|
241
|
+
|
|
242
|
+
## Versioning policy
|
|
243
|
+
|
|
244
|
+
This project follows the semver contract documented in
|
|
245
|
+
[FA-MCP-SDK-DOC/11-public-contract.md](FA-MCP-SDK-DOC/11-public-contract.md#8-versioning-policy-171).
|
|
246
|
+
Mark every `MAJOR` change with `[BREAKING]` in `CHANGELOG.md`.
|
|
247
|
+
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
# ── Image tag (set by CI deploy job) ───────────────────────
|
|
2
|
+
IMAGE_TAG={{project.name}}:latest
|
|
3
|
+
|
|
4
|
+
# ── Host port (external port on the server) ────────────────
|
|
5
|
+
HOST_PORT={{port}}
|
|
6
|
+
|
|
7
|
+
# ── Service identity ──────────────────────────────────────
|
|
8
|
+
SERVICE_NAME={{project.name}}
|
|
9
|
+
SERVICE_INSTANCE=prod
|
|
10
|
+
NODE_ENV=production
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
# ── Stage 1: install + build ──────────────────────────────────────────────────
|
|
2
|
+
FROM node:22-alpine AS builder
|
|
3
|
+
|
|
4
|
+
WORKDIR /app
|
|
5
|
+
|
|
6
|
+
# Зависимости (кэшируется отдельным слоем)
|
|
7
|
+
COPY package.json yarn.lock ./
|
|
8
|
+
RUN yarn install --frozen-lockfile --production=false
|
|
9
|
+
|
|
10
|
+
# Исходники и конфиг сборки
|
|
11
|
+
COPY tsconfig.json ./
|
|
12
|
+
COPY src/ ./src/
|
|
13
|
+
COPY scripts/ ./scripts/
|
|
14
|
+
COPY config/ ./config/
|
|
15
|
+
|
|
16
|
+
# Backend build (tsc → dist/)
|
|
17
|
+
RUN npm run build
|
|
18
|
+
|
|
19
|
+
# ── Stage 2: production image ────────────────────────────────────────────────
|
|
20
|
+
FROM node:22-alpine AS production
|
|
21
|
+
|
|
22
|
+
WORKDIR /app
|
|
23
|
+
|
|
24
|
+
ARG NODE_ENV=production
|
|
25
|
+
ENV NODE_ENV=${NODE_ENV}
|
|
26
|
+
|
|
27
|
+
# Только production-зависимости
|
|
28
|
+
COPY package.json yarn.lock ./
|
|
29
|
+
RUN yarn install --production=true && yarn cache clean
|
|
30
|
+
|
|
31
|
+
# Собранный backend
|
|
32
|
+
COPY --from=builder /app/dist/ ./dist/
|
|
33
|
+
|
|
34
|
+
# Конфиги (default.yaml, production.yaml, custom-environment-variables.yaml и т.д.)
|
|
35
|
+
COPY --from=builder /app/config/ ./config/
|
|
36
|
+
|
|
37
|
+
# Внешние конфиги (local.yaml) монтируются через volumes в docker-compose.yml.
|
|
38
|
+
|
|
39
|
+
EXPOSE {{port}}
|
|
40
|
+
|
|
41
|
+
HEALTHCHECK --interval=30s --timeout=5s --start-period=60s --retries=3 \
|
|
42
|
+
CMD wget --spider -q http://localhost:{{port}}/health || exit 1
|
|
43
|
+
|
|
44
|
+
ENTRYPOINT ["node", "dist/src/start.js"]
|