fa-mcp-sdk 0.4.142 → 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.
Files changed (200) hide show
  1. package/README.md +5 -0
  2. package/cli-template/.dockerignore +16 -0
  3. package/cli-template/.gitlab-ci.yml +135 -0
  4. package/cli-template/AGENTS.md +1 -0
  5. package/cli-template/CHANGELOG.md +64 -0
  6. package/cli-template/FA-MCP-SDK-DOC/00-FA-MCP-SDK-index.md +27 -4
  7. package/cli-template/FA-MCP-SDK-DOC/02-1-tools-and-api.md +195 -0
  8. package/cli-template/FA-MCP-SDK-DOC/02-2-prompts-and-resources.md +172 -9
  9. package/cli-template/FA-MCP-SDK-DOC/03-configuration.md +170 -12
  10. package/cli-template/FA-MCP-SDK-DOC/04-authentication.md +158 -8
  11. package/cli-template/FA-MCP-SDK-DOC/06-utilities.md +67 -6
  12. package/cli-template/FA-MCP-SDK-DOC/07-testing-and-operations.md +31 -15
  13. package/cli-template/FA-MCP-SDK-DOC/10-mcp-apps.md +1 -1
  14. package/cli-template/FA-MCP-SDK-DOC/11-public-contract.md +342 -0
  15. package/cli-template/README.md +37 -0
  16. package/cli-template/deploy/docker/.env.example +10 -0
  17. package/cli-template/deploy/docker/Dockerfile +44 -0
  18. package/cli-template/deploy/docker/Dockerfile.local +29 -0
  19. package/cli-template/deploy/docker/README.md +94 -0
  20. package/cli-template/deploy/docker/config/local.docker.yaml +14 -0
  21. package/cli-template/deploy/docker/docker-compose.yml +31 -0
  22. package/cli-template/deploy/gitlab-runner/.env.example +16 -0
  23. package/cli-template/deploy/gitlab-runner/README.md +65 -0
  24. package/cli-template/deploy/gitlab-runner/config/config.toml.template +26 -0
  25. package/cli-template/deploy/gitlab-runner/docker-compose.yml +39 -0
  26. package/cli-template/deploy/gitlab-runner/entrypoint.sh +27 -0
  27. package/cli-template/deploy/gitlab-runner/start.sh +47 -0
  28. package/cli-template/gitignore +96 -95
  29. package/cli-template/package.json +1 -1
  30. package/config/_local.yaml +73 -11
  31. package/config/custom-environment-variables.yaml +102 -0
  32. package/config/default.yaml +164 -11
  33. package/config/local.yaml +20 -19
  34. package/dist/core/_types_/config.d.ts +119 -0
  35. package/dist/core/_types_/config.d.ts.map +1 -1
  36. package/dist/core/_types_/types.d.ts +137 -4
  37. package/dist/core/_types_/types.d.ts.map +1 -1
  38. package/dist/core/agent-tester/agent-tester-router.d.ts.map +1 -1
  39. package/dist/core/agent-tester/agent-tester-router.js +25 -11
  40. package/dist/core/agent-tester/agent-tester-router.js.map +1 -1
  41. package/dist/core/agent-tester/services/TesterMcpClientService.d.ts.map +1 -1
  42. package/dist/core/agent-tester/services/TesterMcpClientService.js +6 -4
  43. package/dist/core/agent-tester/services/TesterMcpClientService.js.map +1 -1
  44. package/dist/core/auth/admin-auth.js +4 -4
  45. package/dist/core/auth/admin-auth.js.map +1 -1
  46. package/dist/core/auth/agent-tester-auth.d.ts +1 -1
  47. package/dist/core/auth/agent-tester-auth.d.ts.map +1 -1
  48. package/dist/core/auth/agent-tester-auth.js +8 -4
  49. package/dist/core/auth/agent-tester-auth.js.map +1 -1
  50. package/dist/core/auth/auth-profile.d.ts +38 -0
  51. package/dist/core/auth/auth-profile.d.ts.map +1 -0
  52. package/dist/core/auth/auth-profile.js +101 -0
  53. package/dist/core/auth/auth-profile.js.map +1 -0
  54. package/dist/core/auth/jwt-v2.d.ts +27 -0
  55. package/dist/core/auth/jwt-v2.d.ts.map +1 -0
  56. package/dist/core/auth/jwt-v2.js +180 -0
  57. package/dist/core/auth/jwt-v2.js.map +1 -0
  58. package/dist/core/auth/jwt.d.ts +27 -13
  59. package/dist/core/auth/jwt.d.ts.map +1 -1
  60. package/dist/core/auth/jwt.js +36 -13
  61. package/dist/core/auth/jwt.js.map +1 -1
  62. package/dist/core/auth/key-resolver.d.ts +74 -0
  63. package/dist/core/auth/key-resolver.d.ts.map +1 -0
  64. package/dist/core/auth/key-resolver.js +330 -0
  65. package/dist/core/auth/key-resolver.js.map +1 -0
  66. package/dist/core/auth/middleware.d.ts.map +1 -1
  67. package/dist/core/auth/middleware.js +66 -0
  68. package/dist/core/auth/middleware.js.map +1 -1
  69. package/dist/core/auth/multi-auth.d.ts +1 -1
  70. package/dist/core/auth/multi-auth.d.ts.map +1 -1
  71. package/dist/core/auth/multi-auth.js +7 -7
  72. package/dist/core/auth/multi-auth.js.map +1 -1
  73. package/dist/core/auth/token-generator/server.js +4 -4
  74. package/dist/core/auth/token-generator/server.js.map +1 -1
  75. package/dist/core/auth/types.d.ts +5 -0
  76. package/dist/core/auth/types.d.ts.map +1 -1
  77. package/dist/core/db/pg-db.d.ts +7 -0
  78. package/dist/core/db/pg-db.d.ts.map +1 -1
  79. package/dist/core/db/pg-db.js +54 -3
  80. package/dist/core/db/pg-db.js.map +1 -1
  81. package/dist/core/errors/BaseMcpError.d.ts +21 -1
  82. package/dist/core/errors/BaseMcpError.d.ts.map +1 -1
  83. package/dist/core/errors/BaseMcpError.js +20 -1
  84. package/dist/core/errors/BaseMcpError.js.map +1 -1
  85. package/dist/core/errors/ValidationError.d.ts +5 -0
  86. package/dist/core/errors/ValidationError.d.ts.map +1 -1
  87. package/dist/core/errors/ValidationError.js +6 -1
  88. package/dist/core/errors/ValidationError.js.map +1 -1
  89. package/dist/core/errors/errors.d.ts +31 -3
  90. package/dist/core/errors/errors.d.ts.map +1 -1
  91. package/dist/core/errors/errors.js +86 -6
  92. package/dist/core/errors/errors.js.map +1 -1
  93. package/dist/core/errors/specific-errors.d.ts +54 -0
  94. package/dist/core/errors/specific-errors.d.ts.map +1 -0
  95. package/dist/core/errors/specific-errors.js +82 -0
  96. package/dist/core/errors/specific-errors.js.map +1 -0
  97. package/dist/core/index.d.ts +10 -2
  98. package/dist/core/index.d.ts.map +1 -1
  99. package/dist/core/index.js +9 -1
  100. package/dist/core/index.js.map +1 -1
  101. package/dist/core/init-mcp-server.d.ts.map +1 -1
  102. package/dist/core/init-mcp-server.js +39 -0
  103. package/dist/core/init-mcp-server.js.map +1 -1
  104. package/dist/core/mcp/create-mcp-server.d.ts +12 -6
  105. package/dist/core/mcp/create-mcp-server.d.ts.map +1 -1
  106. package/dist/core/mcp/create-mcp-server.js +592 -33
  107. package/dist/core/mcp/create-mcp-server.js.map +1 -1
  108. package/dist/core/mcp/debug-trace.d.ts +3 -1
  109. package/dist/core/mcp/debug-trace.d.ts.map +1 -1
  110. package/dist/core/mcp/debug-trace.js +17 -2
  111. package/dist/core/mcp/debug-trace.js.map +1 -1
  112. package/dist/core/mcp/deprecation.d.ts +31 -0
  113. package/dist/core/mcp/deprecation.d.ts.map +1 -0
  114. package/dist/core/mcp/deprecation.js +96 -0
  115. package/dist/core/mcp/deprecation.js.map +1 -0
  116. package/dist/core/mcp/mcp-logging.d.ts +32 -0
  117. package/dist/core/mcp/mcp-logging.d.ts.map +1 -0
  118. package/dist/core/mcp/mcp-logging.js +97 -0
  119. package/dist/core/mcp/mcp-logging.js.map +1 -0
  120. package/dist/core/mcp/pagination.d.ts +13 -0
  121. package/dist/core/mcp/pagination.d.ts.map +1 -0
  122. package/dist/core/mcp/pagination.js +50 -0
  123. package/dist/core/mcp/pagination.js.map +1 -0
  124. package/dist/core/mcp/prompts.d.ts +5 -1
  125. package/dist/core/mcp/prompts.d.ts.map +1 -1
  126. package/dist/core/mcp/prompts.js +3 -1
  127. package/dist/core/mcp/prompts.js.map +1 -1
  128. package/dist/core/mcp/resources.d.ts +9 -0
  129. package/dist/core/mcp/resources.d.ts.map +1 -1
  130. package/dist/core/mcp/resources.js +158 -11
  131. package/dist/core/mcp/resources.js.map +1 -1
  132. package/dist/core/mcp/server-stdio.d.ts +7 -1
  133. package/dist/core/mcp/server-stdio.d.ts.map +1 -1
  134. package/dist/core/mcp/server-stdio.js +8 -3
  135. package/dist/core/mcp/server-stdio.js.map +1 -1
  136. package/dist/core/mcp/task-store.d.ts +97 -0
  137. package/dist/core/mcp/task-store.d.ts.map +1 -0
  138. package/dist/core/mcp/task-store.js +175 -0
  139. package/dist/core/mcp/task-store.js.map +1 -0
  140. package/dist/core/mcp/tool-limits.d.ts +22 -0
  141. package/dist/core/mcp/tool-limits.d.ts.map +1 -0
  142. package/dist/core/mcp/tool-limits.js +115 -0
  143. package/dist/core/mcp/tool-limits.js.map +1 -0
  144. package/dist/core/mcp/validate-tool-args.d.ts +16 -0
  145. package/dist/core/mcp/validate-tool-args.d.ts.map +1 -0
  146. package/dist/core/mcp/validate-tool-args.js +67 -0
  147. package/dist/core/mcp/validate-tool-args.js.map +1 -0
  148. package/dist/core/mcp/validate-tool-names.d.ts +11 -0
  149. package/dist/core/mcp/validate-tool-names.d.ts.map +1 -0
  150. package/dist/core/mcp/validate-tool-names.js +23 -0
  151. package/dist/core/mcp/validate-tool-names.js.map +1 -0
  152. package/dist/core/metrics/metrics.d.ts +45 -0
  153. package/dist/core/metrics/metrics.d.ts.map +1 -0
  154. package/dist/core/metrics/metrics.js +119 -0
  155. package/dist/core/metrics/metrics.js.map +1 -0
  156. package/dist/core/utils/mask-sensitive.d.ts +44 -0
  157. package/dist/core/utils/mask-sensitive.d.ts.map +1 -0
  158. package/dist/core/utils/mask-sensitive.js +64 -0
  159. package/dist/core/utils/mask-sensitive.js.map +1 -0
  160. package/dist/core/utils/testing/McpHttpClient.d.ts +8 -33
  161. package/dist/core/utils/testing/McpHttpClient.d.ts.map +1 -1
  162. package/dist/core/utils/testing/McpHttpClient.js +8 -74
  163. package/dist/core/utils/testing/McpHttpClient.js.map +1 -1
  164. package/dist/core/utils/testing/McpStreamableHttpClient.d.ts +24 -30
  165. package/dist/core/utils/testing/McpStreamableHttpClient.d.ts.map +1 -1
  166. package/dist/core/utils/testing/McpStreamableHttpClient.js +36 -198
  167. package/dist/core/utils/testing/McpStreamableHttpClient.js.map +1 -1
  168. package/dist/core/utils/utils.d.ts.map +1 -1
  169. package/dist/core/utils/utils.js +2 -0
  170. package/dist/core/utils/utils.js.map +1 -1
  171. package/dist/core/web/admin-router.js +3 -3
  172. package/dist/core/web/admin-router.js.map +1 -1
  173. package/dist/core/web/cors.d.ts +9 -1
  174. package/dist/core/web/cors.d.ts.map +1 -1
  175. package/dist/core/web/cors.js +26 -5
  176. package/dist/core/web/cors.js.map +1 -1
  177. package/dist/core/web/event-store.d.ts +33 -0
  178. package/dist/core/web/event-store.d.ts.map +1 -0
  179. package/dist/core/web/event-store.js +65 -0
  180. package/dist/core/web/event-store.js.map +1 -0
  181. package/dist/core/web/oauth-router.d.ts +37 -0
  182. package/dist/core/web/oauth-router.d.ts.map +1 -0
  183. package/dist/core/web/oauth-router.js +207 -0
  184. package/dist/core/web/oauth-router.js.map +1 -0
  185. package/dist/core/web/request-id.d.ts +44 -0
  186. package/dist/core/web/request-id.d.ts.map +1 -0
  187. package/dist/core/web/request-id.js +82 -0
  188. package/dist/core/web/request-id.js.map +1 -0
  189. package/dist/core/web/server-http.d.ts.map +1 -1
  190. package/dist/core/web/server-http.js +322 -182
  191. package/dist/core/web/server-http.js.map +1 -1
  192. package/package.json +15 -2
  193. package/scripts/claude-2-agents-symlink.js +10 -1
  194. package/scripts/generate-jwt.js +129 -51
  195. package/src/template/custom-resources.ts +14 -0
  196. package/src/template/prompts/custom-prompts.ts +4 -0
  197. package/src/template/tools/handle-tool-call.ts +59 -3
  198. package/src/template/tools/tools.ts +92 -31
  199. package/src/tests/mcp/test-http.js +1 -1
  200. package/src/tests/mcp/test-sse.js +1 -1
package/README.md CHANGED
@@ -16,6 +16,11 @@ This framework provides complete infrastructure for building enterprise-grade MC
16
16
  - **Database Integration**: PostgreSQL with pgvector for vector operations
17
17
  - **Service Discovery**: Consul integration for microservices
18
18
  - **Rate Limiting**: Configurable rate limiting for all endpoints
19
+ - **Transport Hardening**: Per-tool execution timeout, JSON-RPC request body cap and tool-result truncation —
20
+ all driven by `mcp.limits` in `config/default.yaml`:
21
+ - `mcp.limits.maxPayloadBytes` — max accepted request body (default 1 MiB; JSON-RPC `-32005` / HTTP 413 above)
22
+ - `mcp.limits.maxToolResultBytes` — max serialized tool result (default 10 MiB; truncated with explicit marker)
23
+ - `mcp.limits.toolTimeoutMs` — per-tool execution timeout (default 30 000 ms; JSON-RPC `-32004` / HTTP 504 above)
19
24
  - **API Documentation**: Automatic Swagger/OpenAPI generation
20
25
  - **Production Logging**: Structured logging with data masking
21
26
  - **Configuration Management**: YAML-based with environment overrides
@@ -0,0 +1,16 @@
1
+ node_modules
2
+ dist
3
+ .vscode
4
+ .git
5
+ .claude
6
+ .junie
7
+ .run
8
+ deploy
9
+ tests
10
+ coverage
11
+ _logs
12
+ _api_responses
13
+ *.md
14
+ !README.md
15
+ .env*
16
+ !.env.example
@@ -0,0 +1,135 @@
1
+ stages:
2
+ - lint
3
+ - test-build
4
+ - build-image
5
+ - deploy
6
+
7
+ # ── CI: Merge Request checks ──────────────────────────────────────
8
+
9
+ .node-template: &node-template
10
+ image: node:22-alpine
11
+ tags:
12
+ - docker
13
+ before_script:
14
+ - yarn install --frozen-lockfile
15
+ cache:
16
+ key:
17
+ files:
18
+ - yarn.lock
19
+ paths:
20
+ - node_modules/
21
+ rules:
22
+ - if: '$CI_PIPELINE_SOURCE == "merge_request_event"'
23
+
24
+ lint:
25
+ <<: *node-template
26
+ stage: lint
27
+ script:
28
+ - yarn lint
29
+
30
+ test-build:
31
+ <<: *node-template
32
+ stage: test-build
33
+ script:
34
+ - yarn build
35
+
36
+ # ── CD: Build Docker Image ────────────────────────────────────────
37
+
38
+ build-image:
39
+ stage: build-image
40
+ image: docker:27
41
+ services:
42
+ - docker:27-dind
43
+ tags:
44
+ - docker
45
+ script:
46
+ - docker compose -f deploy/docker/docker-compose.yml up -d --build --force-recreate app
47
+ rules:
48
+ - if: '$CI_COMMIT_BRANCH == "master"'
49
+ when: manual
50
+
51
+ # ── CD: Deploy ────────────────────────────────────────────────────
52
+ # Замените <SERVER_TAG> на тег вашего сервера, <BRANCH> на целевую ветку,
53
+ # <DEPLOY_DIR> на путь к проекту на сервере.
54
+
55
+ # deploy_<instance>:
56
+ # stage: deploy
57
+ # tags:
58
+ # - <SERVER_TAG>
59
+ # variables:
60
+ # HOST_PORT: {{port}}
61
+ # environment:
62
+ # name: <instance>
63
+ # script:
64
+ # - docker compose -f deploy/docker/docker-compose.yml --env-file .env up -d --build --force-recreate app
65
+ # - |
66
+ # echo "Waiting for health check..."
67
+ # for i in $(seq 1 60); do
68
+ # if docker compose -f deploy/docker/docker-compose.yml exec -T app wget -q -O /dev/null http://localhost:{{port}}/health 2>/dev/null; then
69
+ # echo "Health check passed after ${i}s"
70
+ # break
71
+ # fi
72
+ # if [ "$i" -eq 60 ]; then
73
+ # echo "Health check failed after 60s"
74
+ # docker compose -f deploy/docker/docker-compose.yml logs --tail=50 app
75
+ # exit 1
76
+ # fi
77
+ # sleep 1
78
+ # done
79
+ # - docker compose -f deploy/docker/docker-compose.yml logs --tail=50 app
80
+ # rules:
81
+ # - if: '$CI_COMMIT_BRANCH == "<BRANCH>"'
82
+ # when: manual
83
+
84
+ # stop_<instance>:
85
+ # stage: deploy
86
+ # tags:
87
+ # - <SERVER_TAG>
88
+ # environment:
89
+ # name: <instance>
90
+ # script:
91
+ # - docker compose -f deploy/docker/docker-compose.yml down
92
+ # rules:
93
+ # - if: '$CI_COMMIT_BRANCH == "<BRANCH>"'
94
+ # when: manual
95
+
96
+ # restart_<instance>:
97
+ # stage: deploy
98
+ # tags:
99
+ # - <SERVER_TAG>
100
+ # variables:
101
+ # HOST_PORT: {{port}}
102
+ # environment:
103
+ # name: <instance>
104
+ # script:
105
+ # - docker compose -f deploy/docker/docker-compose.yml --env-file .env restart app
106
+ # - |
107
+ # echo "Waiting for health check..."
108
+ # for i in $(seq 1 60); do
109
+ # if docker compose -f deploy/docker/docker-compose.yml exec -T app wget -q -O /dev/null http://localhost:{{port}}/health 2>/dev/null; then
110
+ # echo "Health check passed after ${i}s"
111
+ # break
112
+ # fi
113
+ # if [ "$i" -eq 60 ]; then
114
+ # echo "Health check failed after 60s"
115
+ # docker compose -f deploy/docker/docker-compose.yml logs --tail=100 app
116
+ # exit 1
117
+ # fi
118
+ # sleep 1
119
+ # done
120
+ # - docker compose -f deploy/docker/docker-compose.yml logs --tail=100 app
121
+ # rules:
122
+ # - if: '$CI_COMMIT_BRANCH == "<BRANCH>"'
123
+ # when: manual
124
+
125
+ # logs_<instance>:
126
+ # stage: deploy
127
+ # tags:
128
+ # - <SERVER_TAG>
129
+ # environment:
130
+ # name: <instance>
131
+ # script:
132
+ # - docker compose -f deploy/docker/docker-compose.yml logs --tail=100 app
133
+ # rules:
134
+ # - if: '$CI_COMMIT_BRANCH == "<BRANCH>"'
135
+ # when: manual
@@ -131,6 +131,7 @@ Detailed fa-mcp-sdk docs are in `FA-MCP-SDK-DOC/`:
131
131
  | `08-agent-tester-and-headless-api.md` | Agent Tester, Headless API, structured logging, automated testing, MCP Apps mode (capability negotiation, `appCalls[]`, widget rendering, App Inspector) |
132
132
  | `09-database.md` | PostgreSQL sugar layer (`queryMAIN`, `execMAIN`, upserts, `mergeByBatch`), `pgvector`, secondary DBs |
133
133
  | `10-mcp-apps.md` | Building / extending MCP Apps (UI-augmented tools) — protocol contract, SDK surface, patterns, pitfalls |
134
+ | `11-public-contract.md` | Formal SDK public contract — transports, endpoints, JWT claims, tool/prompt/resource format, error mapping, headers, semver & deprecation policy |
134
135
 
135
136
  ## Development and Testing Through Agent Tester
136
137
 
@@ -0,0 +1,64 @@
1
+ # Changelog
2
+
3
+ All notable changes to this MCP server are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/) and the project
6
+ adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). Every breaking change
7
+ MUST be tagged with `[BREAKING]`. See
8
+ [FA-MCP-SDK-DOC/11-public-contract.md](FA-MCP-SDK-DOC/11-public-contract.md#8-versioning-policy-171)
9
+ for the MAJOR / MINOR / PATCH rules.
10
+
11
+ ## Versioning policy (summary)
12
+
13
+ | Change | Bump |
14
+ |-----------------------------------------------------------------|-------|
15
+ | Removing a tool / prompt / resource | MAJOR |
16
+ | Adding a `required` field to an `inputSchema` | MAJOR |
17
+ | Removing a field from an `outputSchema` | MAJOR |
18
+ | Renaming or removing an HTTP endpoint | MAJOR |
19
+ | Adding a new tool / prompt / resource | MINOR |
20
+ | Adding an optional field to a schema | MINOR |
21
+ | Bug-fix without contract impact | PATCH |
22
+
23
+ ## [Unreleased]
24
+
25
+ ### Added
26
+
27
+ - New tool `<name>` — short description (MINOR).
28
+ - New optional argument `<arg>` on tool `<name>` (MINOR).
29
+
30
+ ### Changed
31
+
32
+ - `<tool_name>` description clarified to mention the default value of `<arg>` (PATCH).
33
+
34
+ ### Deprecated
35
+
36
+ - Tool `<old_name>` — replaced by `<new_name>`. Removal scheduled for 2026-08-28
37
+ (≥ 2 MINOR releases or 3 months — whichever comes later, per the public contract). The
38
+ SDK now emits `[DEPRECATED until 2026-08-28, use <new_name>]` on `tools/list` and a
39
+ `logger.warn` the first time per hour an old call lands.
40
+
41
+ ### Removed [BREAKING]
42
+
43
+ - Removed deprecated tool `<old_old_name>` (MAJOR). Migration window closed on 2026-05-01.
44
+
45
+ ### Fixed
46
+
47
+ - Race condition in `<tool>` when called concurrently from multiple sessions (PATCH).
48
+
49
+ ### Security
50
+
51
+ - Bumped `fa-mcp-sdk` to `^0.8.0` for `X-Request-Id` correlation and `traceparent` propagation.
52
+
53
+ ---
54
+
55
+ ## [0.1.0] - YYYY-MM-DD
56
+
57
+ Initial release.
58
+
59
+ ### Added
60
+
61
+ - MCP server scaffolded from `fa-mcp-sdk`.
62
+ - Tools: `<tool>` …
63
+ - Prompts: `agent_brief`, `agent_prompt`.
64
+ - Resources: `project://version`, `use://auth`.
@@ -13,16 +13,17 @@ npm install fa-mcp-sdk
13
13
  | File | Content | Read When |
14
14
  |------|---------|-----------|
15
15
  | [01-getting-started](01-getting-started.md) | `initMcpServer()`, `McpServerData`, `IPromptData`, `IResourceData`, `AppConfig` | Starting new project |
16
- | [02-1-tools-and-api](02-1-tools-and-api.md) | Tool definitions, `toolHandler`, outbound webhooks, REST API with tsoa, OpenAPI/Swagger | Creating tools, REST endpoints, webhook callbacks |
17
- | [02-2-prompts-and-resources](02-2-prompts-and-resources.md) | Standard/custom prompts, resources, `requireAuth` | Configuring prompts/resources |
18
- | [03-configuration](03-configuration.md) | `appConfig`, YAML config, access points for external services, cache | Server configuration, external services |
19
- | [04-authentication](04-authentication.md) | JWT, Basic auth, server tokens, `createAuthMW()`, Token Generator, CLI Token Generator, JWT Generation API | Authentication setup |
16
+ | [02-1-tools-and-api](02-1-tools-and-api.md) | Tool definitions, **snake_case name validation**, **`$schema` 2020-12 + `additionalProperties:false`**, **server-side `arguments` validation**, **`outputSchema` + `structuredContent` mirror**, `toolHandler`, outbound webhooks, REST API with tsoa, OpenAPI/Swagger | Creating tools, REST endpoints, webhook callbacks |
17
+ | [02-2-prompts-and-resources](02-2-prompts-and-resources.md) | Standard/custom prompts, **parameterised prompts (`IPromptArgument[]`)**, resources, **built-in `project://version`, `use://auth`, `<name>://agent/brief\|prompt`**, **opt-in `resources/templates/list` + `resources/subscribe`**, `requireAuth` | Configuring prompts/resources |
18
+ | [03-configuration](03-configuration.md) | `appConfig`, YAML config, access points, cache, **`mcp.limits` (payload/result/timeout)**, **`mcp.pagination`**, **`mcp.resources` (MAY)**, **`mcp.rateLimit.scope` + `maxConcurrentPerSubject` (§14)**, **`webServer.trustProxy`**, **`webServer.tokenCheck.allowQueryToken` (§7.1)**, **/health & /ready**, **CORS hardening**, **MCP error codes** (`-32002…-32005`) | Server configuration, external services, transport-level hardening |
19
+ | [04-authentication](04-authentication.md) | JWT (**4 modes: legacyAesCtr / embedded / localKey / remoteJwks**), Basic auth, server tokens, **OAuth discovery + `/oauth/token` + JWKS**, **`requiredScopes` enforcement (§7.5)**, **`WWW-Authenticate` realm + invalid_token (§7.4)**, **HTTP 403 `forbidden` flag**, `createAuthMW()`, Token Generator, CLI Token Generator (mode-aware), JWT Generation API | Authentication setup |
20
20
  | [05-ad-authorization](05-ad-authorization.md) | AD group authorization at HTTP/tool levels | AD group restrictions |
21
21
  | [06-utilities](06-utilities.md) | `ServerError`, `normalizeHeaders`, logging, MCP debug switches (`DEBUG=mcp:*`), JSON-lines sink (`mcp.debug.logFile` → `emitTrace`), built-in debug tools (`mcp.debug.builtinTools`), Consul, graceful shutdown | Error handling, utilities, request tracing, post-mortem analysis |
22
22
  | [07-testing-and-operations](07-testing-and-operations.md) | Test clients (STDIO, HTTP, SSE, Streamable HTTP); universal `debug-tool` fixture covering every `CallToolResult` shape | Testing, deployment, exercising client code against image/audio/resource/error/delay variants |
23
23
  | [08-agent-tester-and-headless-api](08-agent-tester-and-headless-api.md) | Agent Tester, Headless API, structured logging, automated testing, UI `data-testid` reference. **MCP Apps mode**: capability negotiation, `appCalls[]` / `app_calls[]`, widget iframe bridge, App Inspector tab | Agent-driven tool development, CLI automation, UI E2E tests, MCP Apps host for development |
24
24
  | [09-database](09-database.md) | PostgreSQL sugar layer (`queryMAIN`, `execMAIN`, `getInsertSqlMAIN`, `getMergeSqlMAIN`, `mergeByBatch`), `pgvector`, secondary DBs | Database access, upserts, batching |
25
25
  | [10-mcp-apps](10-mcp-apps.md) | Self-contained digest of the MCP Apps protocol + SDK pinned to `@modelcontextprotocol/ext-apps v1.7.2` (spec 2026-01-26): `ui://` resources, `_meta.ui`, JSON-RPC messages, `App` class, host context, patterns, pitfalls. **Canonical example** (`examples/mcp-apps-canonical/`, `npm run example:mcp-apps`) and widget-side debug helpers (`mcp-debug-log`, `mcp-debug-refresh`). Cross-links to Agent Tester as a dev-host (doc 08) | Building / extending MCP Apps (UI-augmented tools) |
26
+ | [11-public-contract](11-public-contract.md) | Formal public-contract surface of `fa-mcp-sdk`: transports, HTTP endpoints, JWT claims, tool/prompt/resource shape, error mapping, limits & headers (`X-Request-Id`, `traceparent`, `Retry-After`, `WWW-Authenticate`, `MCP-Session-Id`), semver policy, deprecation process | Pinning SDK version, planning a SDK upgrade, drafting CHANGELOG entries, deciding MAJOR vs MINOR vs PATCH |
26
27
 
27
28
  ## Key Exports
28
29
 
@@ -40,6 +41,28 @@ import {
40
41
  getJsonFromResult,
41
42
  TToolHandlerResponse, IToolHandlerTextResponse, IToolHandlerStructuredResponse,
42
43
  ToolExecutionError, ServerError, BaseMcpError, ValidationError, getTools,
44
+ // Appendix B errors — emitted by the SDK transport; re-throwable from tool / API code
45
+ PayloadTooLargeError, TimeoutError, RateLimitedError, ResourceNotFoundError,
46
+ MCP_ERROR_CODES, IMcpErrorData, createJsonRpcErrorResponse,
47
+ } from 'fa-mcp-sdk';
48
+
49
+ // Prompts (parameterised — standard §10.5) & Resources (templates/subscribe — §11.5)
50
+ import {
51
+ IPromptArgument, IPromptData,
52
+ IIcon, // §10.5/§11.3 — optional title/icons UI metadata
53
+ IResourceTemplateInfo,
54
+ notifyResourceUpdated, // call to broadcast `notifications/resources/updated`
55
+ } from 'fa-mcp-sdk';
56
+
57
+ // SSE resumability (standard §6) — opt-in via mcp.sse.resumability; maskSensitive (§12.2) — opt-in
58
+ // result masking, applied by the server inside a tool handler.
59
+ import { InMemoryEventStore, maskSensitive, IMaskRules } from 'fa-mcp-sdk';
60
+
61
+ // Task-augmented execution (standard §8.7) — opt-in via mcp.tasks.enabled; declare a long-running
62
+ // tool with execution.taskSupport. Storage is pluggable (default: in-memory, per-process).
63
+ import {
64
+ getTaskStore, resetTaskStore, InMemoryTaskStore, toTaskDto, isTerminalTaskStatus,
65
+ ITaskStore, ITaskRecord, ITaskCreateInput, ITaskStoreOptions, TTaskStatus, TTaskPatch,
43
66
  } from 'fa-mcp-sdk';
44
67
 
45
68
  // Database & Cache
@@ -9,14 +9,67 @@ import { Tool } from '@modelcontextprotocol/sdk/types.js';
9
9
 
10
10
  export const tools: Tool[] = [{
11
11
  name: 'my_custom_tool',
12
+ title: 'My custom tool', // SHOULD §9.1 — human-readable name
12
13
  description: 'Description of what this tool does',
13
14
  inputSchema: {
15
+ $schema: 'https://json-schema.org/draft/2020-12/schema', // standard §9.2
14
16
  type: 'object',
15
17
  properties: {
16
18
  query: { type: 'string', description: 'Input query' },
17
19
  options: { type: 'object', description: 'Optional config' },
18
20
  },
19
21
  required: ['query'],
22
+ additionalProperties: false, // reject unknown fields
23
+ },
24
+ }];
25
+ ```
26
+
27
+ **Standard §9.1 (MUST) — tool name `name` MUST match `/^[a-z][a-z0-9_]{0,62}$/`** (ASCII
28
+ snake_case, 1..63 chars). The SDK validates names eagerly at `initMcpServer()` for static
29
+ tool arrays and lazily on the first `getTools()` call for dynamic (function-form) tools — a
30
+ violation throws with the offending name printed.
31
+
32
+ **Standard §9.2 — `inputSchema` SHOULD declare `$schema: '…/draft/2020-12/schema'` and
33
+ `additionalProperties: false`.** Both fields are recognised by the `IToolInputSchema` type.
34
+
35
+ **Standard §9.3 (MUST) — arguments are validated server-side.** Before `toolHandler` is
36
+ called, the SDK validates `request.params.arguments` against `inputSchema` via ajv (draft
37
+ 2020-12). On failure the response is JSON-RPC `-32602` with
38
+ `error.data = { field, reason }`; the handler is **not** invoked. This means tool code
39
+ no longer needs to repeat shape checks — by the time the handler runs, `args` already
40
+ matches the schema.
41
+
42
+ ### Output schema and `structuredContent` (standard §9.4 / §12.4)
43
+
44
+ A tool MAY declare `outputSchema` to describe its `structuredContent` payload. When set,
45
+ the SDK validates the handler's response against the schema — a violation raises JSON-RPC
46
+ `-32603` (internal error: the tool broke its own contract). Whenever a response includes
47
+ `structuredContent`, the SDK mirrors a serialised JSON copy into `content[0].text` so
48
+ legacy clients that only read `content` keep working without code changes.
49
+
50
+ ```typescript
51
+ export const tools: Tool[] = [{
52
+ name: 'search_docs',
53
+ title: 'Search documents',
54
+ description: 'Vector search over the knowledge base.',
55
+ inputSchema: { /* …as above… */ },
56
+ outputSchema: {
57
+ $schema: 'https://json-schema.org/draft/2020-12/schema',
58
+ type: 'object',
59
+ properties: {
60
+ results: {
61
+ type: 'array',
62
+ items: {
63
+ type: 'object',
64
+ properties: { id: { type: 'string' }, score: { type: 'number' } },
65
+ required: ['id'],
66
+ additionalProperties: true,
67
+ },
68
+ },
69
+ total: { type: 'number' },
70
+ },
71
+ required: ['results'],
72
+ additionalProperties: true,
20
73
  },
21
74
  }];
22
75
  ```
@@ -135,6 +188,148 @@ const clientIP = headers?.['x-real-ip'] || headers?.['x-forwarded-for'];
135
188
  `clientCapabilities`). See
136
189
  [ITransportContext](./02-2-prompts-and-resources.md#itransportcontext).
137
190
 
191
+ ### Cancellation (`signal`) — standard §8.5
192
+
193
+ `IToolHandlerParams.signal?: AbortSignal` is flipped when the client sends
194
+ `notifications/cancelled` for the current request. Pass it straight to any downstream
195
+ `AbortSignal`-aware API (`fetch`, `pg`, `axios` ≥ 0.22, …) — they will abort their work and
196
+ let the rejection propagate. Tool handlers MUST stop work once the signal aborts; the SDK
197
+ then suppresses the JSON-RPC response per §8.5.
198
+
199
+ ```typescript
200
+ export const handleToolCall = async (params: IToolHandlerParams): Promise<TToolHandlerResponse> => {
201
+ const { name, arguments: args, signal } = params;
202
+
203
+ switch (name) {
204
+ case 'search_documents': {
205
+ // Native AbortSignal forwarding — fetch will throw AbortError when the client cancels.
206
+ const res = await fetch(`https://docs.example.com/search?q=${encodeURIComponent(args.q)}`, {
207
+ signal,
208
+ });
209
+ const items = await res.json();
210
+ return formatToolResult({ items });
211
+ }
212
+ }
213
+ };
214
+ ```
215
+
216
+ For libraries that do not understand `AbortSignal` natively, gate the work with
217
+ `signal.aborted` checks at safe seams (between DB pages, loop iterations, retry attempts):
218
+
219
+ ```typescript
220
+ case 'long_running': {
221
+ for (const chunk of chunks) {
222
+ if (signal?.aborted) {
223
+ throw signal.reason ?? new Error('cancelled');
224
+ }
225
+ await process(chunk);
226
+ }
227
+ return formatToolResult({ ok: true });
228
+ }
229
+ ```
230
+
231
+ When `signal` is `undefined` (legacy transports or older SDK consumers), behave as if it were
232
+ never aborted — handlers should remain forward-compatible.
233
+
234
+ ### Progress (`sendProgress`) — standard §8.6
235
+
236
+ `IToolHandlerParams.sendProgress?` emits `notifications/progress` whenever the request
237
+ carried `_meta.progressToken`. When the client did not request progress, the SDK passes a
238
+ no-op so the handler can call it unconditionally — no `if` guard needed.
239
+
240
+ Rules enforced server-side:
241
+
242
+ - progress values MUST be monotonically non-decreasing (smaller values are silently dropped);
243
+ - emissions are throttled by `mcp.progress.throttleMs` (default 100 ms → max 10 events/s).
244
+
245
+ ```typescript
246
+ case 'bulk_import': {
247
+ const rows = await loadRows(args.source);
248
+ for (let i = 0; i < rows.length; i++) {
249
+ if (signal?.aborted) {
250
+ throw signal.reason ?? new Error('cancelled');
251
+ }
252
+ await importRow(rows[i]);
253
+ sendProgress?.(i + 1, rows.length, `imported ${rows[i].id}`);
254
+ }
255
+ return formatToolResult({ inserted: rows.length });
256
+ }
257
+ ```
258
+
259
+ The client receives:
260
+
261
+ ```json
262
+ {
263
+ "method": "notifications/progress",
264
+ "params": {
265
+ "progressToken": "abc-123",
266
+ "progress": 42,
267
+ "total": 100,
268
+ "message": "imported acct-42"
269
+ }
270
+ }
271
+ ```
272
+
273
+ Choose `total` only when the upper bound is known up-front; otherwise omit it and the client
274
+ will render an indeterminate spinner.
275
+
276
+ ### Task-augmented execution (long-running tools) — standard §8.7
277
+
278
+ A normal `tools/call` is synchronous: the client holds the connection open until the tool returns,
279
+ and the call is bound by the tool timeout (`mcp.limits.toolTimeoutMs`, 30 seconds by default). For
280
+ operations that legitimately take minutes — bulk exports, report generation, long searches — the
281
+ SDK supports **task-augmented execution**: the server returns a task identifier immediately and runs
282
+ the tool in the background; the client then polls for status and fetches the result when ready.
283
+
284
+ This feature is **opt-in and off by default**. To enable it:
285
+
286
+ 1. Set `mcp.tasks.enabled: true` in the configuration. The server then advertises the `tasks`
287
+ capability and accepts the lifecycle methods `tasks/list`, `tasks/get`, `tasks/result` and
288
+ `tasks/cancel`.
289
+ 2. Mark the long-running tool with `execution.taskSupport` in its declaration:
290
+
291
+ ```typescript
292
+ {
293
+ name: 'generate_report',
294
+ title: 'Generate a large report',
295
+ description: 'Builds a multi-page report. Long-running — call it as a task.',
296
+ inputSchema: { /* … */ },
297
+ // 'optional' — the client MAY ask for a task but can still call synchronously.
298
+ // 'required' — the tool runs only as a task (a synchronous call is rejected with -32602).
299
+ // 'forbidden' / omitted — synchronous only.
300
+ execution: { taskSupport: 'optional' },
301
+ }
302
+ ```
303
+
304
+ The same handler runs whether the tool is invoked synchronously or as a task — the SDK always
305
+ supplies `signal` and `sendProgress`. When the tool runs as a task, `signal` is flipped by
306
+ `tasks/cancel`, progress is delivered through `notifications/progress`, and the SDK emits a
307
+ `notifications/tasks/status` on every status change. On completion the task transitions to
308
+ `completed` (carrying the same result a synchronous call would return); on a thrown error it
309
+ transitions to `failed` with a sanitized message; on cancellation it transitions to `cancelled`.
310
+
311
+ The client drives the lifecycle by sending a `task` parameter on `tools/call` and then polling:
312
+
313
+ ```jsonc
314
+ // 1. Create — returns immediately with { task: { taskId, status: "working", … } }
315
+ { "method": "tools/call", "params": { "name": "generate_report", "arguments": {}, "task": {} } }
316
+
317
+ // 2. Poll status until terminal
318
+ { "method": "tasks/get", "params": { "taskId": "…" } } // → { status: "working" | "completed" | … }
319
+
320
+ // 3. Fetch the result once completed (same shape a synchronous tools/call returns)
321
+ { "method": "tasks/result", "params": { "taskId": "…" } }
322
+
323
+ // Optional — abort a running task
324
+ { "method": "tasks/cancel", "params": { "taskId": "…" } }
325
+ ```
326
+
327
+ The default task store keeps records **in process memory only** — it does not survive a server
328
+ restart, and it is scoped to a single instance (no shared store across a cluster). Retention,
329
+ poll interval and the retained-task cap are configured under `mcp.tasks.*` (see
330
+ [03-configuration.md](./03-configuration.md)); the full method contract is in
331
+ [11-public-contract.md](./11-public-contract.md) §4.
332
+
138
333
  ### MCP Apps — Reading Client Capabilities
139
334
 
140
335
  `params.clientCapabilities` carries the client's `initialize`-time capabilities (including the