ouncemcp 1.0.0rc1__tar.gz

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 (332) hide show
  1. ouncemcp-1.0.0rc1/.gitignore +16 -0
  2. ouncemcp-1.0.0rc1/CHANGELOG.md +39 -0
  3. ouncemcp-1.0.0rc1/CONTRIBUTING.md +32 -0
  4. ouncemcp-1.0.0rc1/LICENSE +202 -0
  5. ouncemcp-1.0.0rc1/PKG-INFO +232 -0
  6. ouncemcp-1.0.0rc1/README.md +182 -0
  7. ouncemcp-1.0.0rc1/SECURITY.md +15 -0
  8. ouncemcp-1.0.0rc1/benchmarks/README.md +14 -0
  9. ouncemcp-1.0.0rc1/benchmarks/bench.yaml +31 -0
  10. ouncemcp-1.0.0rc1/benchmarks/results/.gitkeep +0 -0
  11. ouncemcp-1.0.0rc1/benchmarks/results/gate6-release-check.json +5148 -0
  12. ouncemcp-1.0.0rc1/benchmarks/results/m0-inventory-tokens.json +38 -0
  13. ouncemcp-1.0.0rc1/benchmarks/results/m0-overhead.json +43 -0
  14. ouncemcp-1.0.0rc1/benchmarks/results/m1-truncation.json +34 -0
  15. ouncemcp-1.0.0rc1/benchmarks/results/m3-bench.json +4931 -0
  16. ouncemcp-1.0.0rc1/benchmarks/results/m3-bench.md +66 -0
  17. ouncemcp-1.0.0rc1/benchmarks/results/m4-perf.json +96 -0
  18. ouncemcp-1.0.0rc1/benchmarks/scripts/.gitkeep +0 -0
  19. ouncemcp-1.0.0rc1/benchmarks/scripts/_common.py +12 -0
  20. ouncemcp-1.0.0rc1/benchmarks/scripts/m0_compare.py +90 -0
  21. ouncemcp-1.0.0rc1/benchmarks/scripts/m0_overhead.py +119 -0
  22. ouncemcp-1.0.0rc1/benchmarks/scripts/m1_truncation.py +118 -0
  23. ouncemcp-1.0.0rc1/benchmarks/scripts/m4_release_perf.py +294 -0
  24. ouncemcp-1.0.0rc1/benchmarks/servers/.gitkeep +0 -0
  25. ouncemcp-1.0.0rc1/benchmarks/tasks/.gitkeep +0 -0
  26. ouncemcp-1.0.0rc1/benchmarks/tasks/inventory.yaml +123 -0
  27. ouncemcp-1.0.0rc1/benchmarks/tasks/ticketing.yaml +133 -0
  28. ouncemcp-1.0.0rc1/benchmarks/tasks/ticketing_checks.py +30 -0
  29. ouncemcp-1.0.0rc1/conformance/expected-failures.yml +22 -0
  30. ouncemcp-1.0.0rc1/docs/README.md +95 -0
  31. ouncemcp-1.0.0rc1/docs/assets/extra.css +23 -0
  32. ouncemcp-1.0.0rc1/docs/assets/favicon.svg +1 -0
  33. ouncemcp-1.0.0rc1/docs/book/appendix-a-cli.md +22 -0
  34. ouncemcp-1.0.0rc1/docs/book/appendix-b-errors-lints.md +33 -0
  35. ouncemcp-1.0.0rc1/docs/book/appendix-c-glossary.md +45 -0
  36. ouncemcp-1.0.0rc1/docs/book/ch01-getting-started.md +69 -0
  37. ouncemcp-1.0.0rc1/docs/book/ch02-first-server.md +112 -0
  38. ouncemcp-1.0.0rc1/docs/book/ch03-budgets.md +100 -0
  39. ouncemcp-1.0.0rc1/docs/book/ch04-shaping.md +77 -0
  40. ouncemcp-1.0.0rc1/docs/book/ch05-errors.md +60 -0
  41. ouncemcp-1.0.0rc1/docs/book/ch06-writes.md +78 -0
  42. ouncemcp-1.0.0rc1/docs/book/ch07-security.md +66 -0
  43. ouncemcp-1.0.0rc1/docs/book/ch08-jobs.md +55 -0
  44. ouncemcp-1.0.0rc1/docs/book/ch09-resources-prompts.md +55 -0
  45. ouncemcp-1.0.0rc1/docs/book/ch10-testing.md +77 -0
  46. ouncemcp-1.0.0rc1/docs/book/ch11-measuring.md +89 -0
  47. ouncemcp-1.0.0rc1/docs/book/ch12-discovery.md +55 -0
  48. ouncemcp-1.0.0rc1/docs/book/ch13-deploying.md +63 -0
  49. ouncemcp-1.0.0rc1/docs/book/ch14-migrating.md +38 -0
  50. ouncemcp-1.0.0rc1/docs/book/ch15-clients.md +33 -0
  51. ouncemcp-1.0.0rc1/docs/book/index.md +35 -0
  52. ouncemcp-1.0.0rc1/docs/changelog.md +1 -0
  53. ouncemcp-1.0.0rc1/docs/compatibility.md +28 -0
  54. ouncemcp-1.0.0rc1/docs/concepts/budgets.md +21 -0
  55. ouncemcp-1.0.0rc1/docs/concepts/discovery.md +10 -0
  56. ouncemcp-1.0.0rc1/docs/concepts/execution.md +14 -0
  57. ouncemcp-1.0.0rc1/docs/concepts/resources-prompts.md +22 -0
  58. ouncemcp-1.0.0rc1/docs/concepts/security.md +37 -0
  59. ouncemcp-1.0.0rc1/docs/concepts/shaping.md +17 -0
  60. ouncemcp-1.0.0rc1/docs/concepts/traces.md +13 -0
  61. ouncemcp-1.0.0rc1/docs/errors/ambiguous_write.md +13 -0
  62. ouncemcp-1.0.0rc1/docs/errors/authentication.md +12 -0
  63. ouncemcp-1.0.0rc1/docs/errors/internal.md +11 -0
  64. ouncemcp-1.0.0rc1/docs/errors/not_found.md +13 -0
  65. ouncemcp-1.0.0rc1/docs/errors/permission.md +16 -0
  66. ouncemcp-1.0.0rc1/docs/errors/rate_limit.md +13 -0
  67. ouncemcp-1.0.0rc1/docs/errors/transient.md +14 -0
  68. ouncemcp-1.0.0rc1/docs/errors/validation.md +20 -0
  69. ouncemcp-1.0.0rc1/docs/guides/client-broker.md +23 -0
  70. ouncemcp-1.0.0rc1/docs/guides/deploying-http.md +52 -0
  71. ouncemcp-1.0.0rc1/docs/guides/evaluating.md +47 -0
  72. ouncemcp-1.0.0rc1/docs/guides/host-verification.md +58 -0
  73. ouncemcp-1.0.0rc1/docs/guides/migrating-from-fastmcp.md +64 -0
  74. ouncemcp-1.0.0rc1/docs/guides/safe-writes.md +19 -0
  75. ouncemcp-1.0.0rc1/docs/guides/securing-tools.md +15 -0
  76. ouncemcp-1.0.0rc1/docs/guides/testing.md +33 -0
  77. ouncemcp-1.0.0rc1/docs/m0-report.md +85 -0
  78. ouncemcp-1.0.0rc1/docs/m2-report.md +54 -0
  79. ouncemcp-1.0.0rc1/docs/m4-report.md +60 -0
  80. ouncemcp-1.0.0rc1/docs/maintainers/releasing.md +47 -0
  81. ouncemcp-1.0.0rc1/docs/maintainers/repository-settings.md +73 -0
  82. ouncemcp-1.0.0rc1/docs/maintainers/security-review.md +82 -0
  83. ouncemcp-1.0.0rc1/docs/mcp-token-footprint.png +0 -0
  84. ouncemcp-1.0.0rc1/docs/performance-research.md +171 -0
  85. ouncemcp-1.0.0rc1/docs/proposals/0001-m0-core-api.md +49 -0
  86. ouncemcp-1.0.0rc1/docs/proposals/0002-schema-safe-shaping-and-pagination.md +138 -0
  87. ouncemcp-1.0.0rc1/docs/proposals/0003-signed-cursors.md +112 -0
  88. ouncemcp-1.0.0rc1/docs/proposals/0004-pluggable-tokenizer.md +67 -0
  89. ouncemcp-1.0.0rc1/docs/proposals/0005-counting-scope-and-output-schema-modes.md +89 -0
  90. ouncemcp-1.0.0rc1/docs/proposals/0006-discovery-modes.md +126 -0
  91. ouncemcp-1.0.0rc1/docs/proposals/0007-errors-and-execution-limits.md +114 -0
  92. ouncemcp-1.0.0rc1/docs/proposals/0008-security-and-policy.md +80 -0
  93. ouncemcp-1.0.0rc1/docs/proposals/0009-traces.md +46 -0
  94. ouncemcp-1.0.0rc1/docs/proposals/0010-m2-plan.md +75 -0
  95. ouncemcp-1.0.0rc1/docs/proposals/0011-store-and-idempotency.md +58 -0
  96. ouncemcp-1.0.0rc1/docs/proposals/0012-safe-writes.md +50 -0
  97. ouncemcp-1.0.0rc1/docs/proposals/0013-handles-and-jobs.md +32 -0
  98. ouncemcp-1.0.0rc1/docs/proposals/0014-resources-and-prompts.md +39 -0
  99. ouncemcp-1.0.0rc1/docs/proposals/0015-migration-and-audit.md +43 -0
  100. ouncemcp-1.0.0rc1/docs/proposals/0016-client-broker.md +46 -0
  101. ouncemcp-1.0.0rc1/docs/proposals/0017-operations.md +26 -0
  102. ouncemcp-1.0.0rc1/docs/proposals/0018-trace-viewer.md +26 -0
  103. ouncemcp-1.0.0rc1/docs/proposals/0019-eval-kit.md +48 -0
  104. ouncemcp-1.0.0rc1/docs/proposals/0020-report-ui.md +51 -0
  105. ouncemcp-1.0.0rc1/docs/proposals/0021-budget-overrides.md +36 -0
  106. ouncemcp-1.0.0rc1/docs/proposals/0022-parallel-safe-tools.md +32 -0
  107. ouncemcp-1.0.0rc1/docs/proposals/0023-safe-fetch.md +40 -0
  108. ouncemcp-1.0.0rc1/docs/proposals/0024-context-and-audit-pages.md +33 -0
  109. ouncemcp-1.0.0rc1/docs/proposals/0025-oauth.md +37 -0
  110. ouncemcp-1.0.0rc1/docs/proposals/0026-real-model-evals.md +48 -0
  111. ouncemcp-1.0.0rc1/docs/proposals/0027-benchmark.md +50 -0
  112. ouncemcp-1.0.0rc1/docs/proposals/0028-resumable-jobs.md +47 -0
  113. ouncemcp-1.0.0rc1/docs/proposals/0029-api-freeze.md +86 -0
  114. ouncemcp-1.0.0rc1/docs/proposals/README.md +5 -0
  115. ouncemcp-1.0.0rc1/docs/quickstart.md +81 -0
  116. ouncemcp-1.0.0rc1/docs/reference/budgets.md +29 -0
  117. ouncemcp-1.0.0rc1/docs/reference/client.md +12 -0
  118. ouncemcp-1.0.0rc1/docs/reference/errors.md +26 -0
  119. ouncemcp-1.0.0rc1/docs/reference/eval.md +51 -0
  120. ouncemcp-1.0.0rc1/docs/reference/execution.md +24 -0
  121. ouncemcp-1.0.0rc1/docs/reference/index.md +15 -0
  122. ouncemcp-1.0.0rc1/docs/reference/security.md +8 -0
  123. ouncemcp-1.0.0rc1/docs/reference/server.md +30 -0
  124. ouncemcp-1.0.0rc1/docs/reference/testing.md +6 -0
  125. ouncemcp-1.0.0rc1/docs/reference/trace.md +24 -0
  126. ouncemcp-1.0.0rc1/docs/rules/budget-override-without-reason.md +25 -0
  127. ouncemcp-1.0.0rc1/docs/rules/description-too-short.md +15 -0
  128. ouncemcp-1.0.0rc1/docs/rules/destructive-without-policy.md +15 -0
  129. ouncemcp-1.0.0rc1/docs/rules/elicit-in-write.md +15 -0
  130. ouncemcp-1.0.0rc1/docs/rules/free-form-input.md +15 -0
  131. ouncemcp-1.0.0rc1/docs/rules/inconsistent-tool-names.md +15 -0
  132. ouncemcp-1.0.0rc1/docs/rules/input-after-effect.md +27 -0
  133. ouncemcp-1.0.0rc1/docs/rules/missing-usage-guidance.md +15 -0
  134. ouncemcp-1.0.0rc1/docs/rules/stdio-skips-policy.md +16 -0
  135. ouncemcp-1.0.0rc1/docs/rules/undocumented-parameter.md +21 -0
  136. ouncemcp-1.0.0rc1/docs/rules/unpaginated-list.md +23 -0
  137. ouncemcp-1.0.0rc1/docs/rules/write-without-side-effects.md +15 -0
  138. ouncemcp-1.0.0rc1/docs/spec.md +1045 -0
  139. ouncemcp-1.0.0rc1/examples/README.md +13 -0
  140. ouncemcp-1.0.0rc1/examples/book/shelf/README.md +19 -0
  141. ouncemcp-1.0.0rc1/examples/book/shelf/eval.yaml +62 -0
  142. ouncemcp-1.0.0rc1/examples/book/shelf/eval_checks.py +13 -0
  143. ouncemcp-1.0.0rc1/examples/book/shelf/library.py +109 -0
  144. ouncemcp-1.0.0rc1/examples/book/shelf/step1_catalog.py +80 -0
  145. ouncemcp-1.0.0rc1/examples/book/shelf/step2_shaped.py +128 -0
  146. ouncemcp-1.0.0rc1/examples/book/shelf/step3_writes.py +192 -0
  147. ouncemcp-1.0.0rc1/examples/book/shelf/step4_secure.py +217 -0
  148. ouncemcp-1.0.0rc1/examples/book/shelf/step5_complete.py +293 -0
  149. ouncemcp-1.0.0rc1/examples/inventory/README.md +50 -0
  150. ouncemcp-1.0.0rc1/examples/inventory/data.py +72 -0
  151. ouncemcp-1.0.0rc1/examples/inventory/eval.yaml +93 -0
  152. ouncemcp-1.0.0rc1/examples/inventory/eval_checks.py +24 -0
  153. ouncemcp-1.0.0rc1/examples/inventory/plain_server.py +38 -0
  154. ouncemcp-1.0.0rc1/examples/inventory/server.py +101 -0
  155. ouncemcp-1.0.0rc1/examples/inventory/workflow.py +101 -0
  156. ouncemcp-1.0.0rc1/examples/markitdown/README.md +28 -0
  157. ouncemcp-1.0.0rc1/examples/markitdown/converter.py +31 -0
  158. ouncemcp-1.0.0rc1/examples/markitdown/original.py +39 -0
  159. ouncemcp-1.0.0rc1/examples/markitdown/server.py +98 -0
  160. ouncemcp-1.0.0rc1/examples/markitdown/step1.py +17 -0
  161. ouncemcp-1.0.0rc1/examples/ouncemcp.example.toml +18 -0
  162. ouncemcp-1.0.0rc1/examples/report/README.md +26 -0
  163. ouncemcp-1.0.0rc1/examples/report/server.py +101 -0
  164. ouncemcp-1.0.0rc1/examples/ticketing/README.md +42 -0
  165. ouncemcp-1.0.0rc1/examples/ticketing/eval.yaml +96 -0
  166. ouncemcp-1.0.0rc1/examples/ticketing/eval_checks.py +26 -0
  167. ouncemcp-1.0.0rc1/examples/ticketing/plain_server.py +50 -0
  168. ouncemcp-1.0.0rc1/examples/ticketing/server.py +160 -0
  169. ouncemcp-1.0.0rc1/examples/ticketing/upstream.py +70 -0
  170. ouncemcp-1.0.0rc1/mkdocs.yml +209 -0
  171. ouncemcp-1.0.0rc1/pyproject.toml +95 -0
  172. ouncemcp-1.0.0rc1/src/ouncemcp/__init__.py +99 -0
  173. ouncemcp-1.0.0rc1/src/ouncemcp/__main__.py +3 -0
  174. ouncemcp-1.0.0rc1/src/ouncemcp/audit/__init__.py +21 -0
  175. ouncemcp-1.0.0rc1/src/ouncemcp/audit/runner.py +392 -0
  176. ouncemcp-1.0.0rc1/src/ouncemcp/cli/__init__.py +662 -0
  177. ouncemcp-1.0.0rc1/src/ouncemcp/cli/dev.py +248 -0
  178. ouncemcp-1.0.0rc1/src/ouncemcp/cli/loader.py +52 -0
  179. ouncemcp-1.0.0rc1/src/ouncemcp/cli/scaffold.py +252 -0
  180. ouncemcp-1.0.0rc1/src/ouncemcp/client/__init__.py +25 -0
  181. ouncemcp-1.0.0rc1/src/ouncemcp/client/broker.py +507 -0
  182. ouncemcp-1.0.0rc1/src/ouncemcp/client/oauth.py +83 -0
  183. ouncemcp-1.0.0rc1/src/ouncemcp/core/__init__.py +37 -0
  184. ouncemcp-1.0.0rc1/src/ouncemcp/core/budget.py +88 -0
  185. ouncemcp-1.0.0rc1/src/ouncemcp/core/config.py +112 -0
  186. ouncemcp-1.0.0rc1/src/ouncemcp/core/costs.py +237 -0
  187. ouncemcp-1.0.0rc1/src/ouncemcp/core/cursor.py +246 -0
  188. ouncemcp-1.0.0rc1/src/ouncemcp/core/docs.py +20 -0
  189. ouncemcp-1.0.0rc1/src/ouncemcp/core/effects.py +45 -0
  190. ouncemcp-1.0.0rc1/src/ouncemcp/core/errors.py +532 -0
  191. ouncemcp-1.0.0rc1/src/ouncemcp/core/lints.py +429 -0
  192. ouncemcp-1.0.0rc1/src/ouncemcp/core/pagination.py +88 -0
  193. ouncemcp-1.0.0rc1/src/ouncemcp/core/primitives.py +240 -0
  194. ouncemcp-1.0.0rc1/src/ouncemcp/core/provider_tokens.py +97 -0
  195. ouncemcp-1.0.0rc1/src/ouncemcp/core/schema.py +45 -0
  196. ouncemcp-1.0.0rc1/src/ouncemcp/core/server.py +1863 -0
  197. ouncemcp-1.0.0rc1/src/ouncemcp/core/shaping.py +811 -0
  198. ouncemcp-1.0.0rc1/src/ouncemcp/core/tokens.py +167 -0
  199. ouncemcp-1.0.0rc1/src/ouncemcp/core/wire.py +13 -0
  200. ouncemcp-1.0.0rc1/src/ouncemcp/discovery/__init__.py +40 -0
  201. ouncemcp-1.0.0rc1/src/ouncemcp/discovery/modes.py +103 -0
  202. ouncemcp-1.0.0rc1/src/ouncemcp/discovery/recall.py +97 -0
  203. ouncemcp-1.0.0rc1/src/ouncemcp/discovery/search.py +209 -0
  204. ouncemcp-1.0.0rc1/src/ouncemcp/eval/__init__.py +31 -0
  205. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/__init__.py +25 -0
  206. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/model.py +153 -0
  207. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/openrouter.py +273 -0
  208. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/report.py +282 -0
  209. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/runner.py +383 -0
  210. ouncemcp-1.0.0rc1/src/ouncemcp/eval/agent/suite.py +261 -0
  211. ouncemcp-1.0.0rc1/src/ouncemcp/eval/bench.py +507 -0
  212. ouncemcp-1.0.0rc1/src/ouncemcp/eval/conformance.py +291 -0
  213. ouncemcp-1.0.0rc1/src/ouncemcp/eval/security.py +312 -0
  214. ouncemcp-1.0.0rc1/src/ouncemcp/execution/__init__.py +26 -0
  215. ouncemcp-1.0.0rc1/src/ouncemcp/execution/approval.py +159 -0
  216. ouncemcp-1.0.0rc1/src/ouncemcp/execution/handles.py +278 -0
  217. ouncemcp-1.0.0rc1/src/ouncemcp/execution/idempotency.py +268 -0
  218. ouncemcp-1.0.0rc1/src/ouncemcp/execution/limits.py +114 -0
  219. ouncemcp-1.0.0rc1/src/ouncemcp/execution/middleware.py +161 -0
  220. ouncemcp-1.0.0rc1/src/ouncemcp/execution/resolver.py +32 -0
  221. ouncemcp-1.0.0rc1/src/ouncemcp/execution/retry.py +53 -0
  222. ouncemcp-1.0.0rc1/src/ouncemcp/execution/runner.py +60 -0
  223. ouncemcp-1.0.0rc1/src/ouncemcp/execution/store.py +175 -0
  224. ouncemcp-1.0.0rc1/src/ouncemcp/py.typed +0 -0
  225. ouncemcp-1.0.0rc1/src/ouncemcp/security/__init__.py +112 -0
  226. ouncemcp-1.0.0rc1/src/ouncemcp/security/cache.py +117 -0
  227. ouncemcp-1.0.0rc1/src/ouncemcp/security/fetch.py +115 -0
  228. ouncemcp-1.0.0rc1/src/ouncemcp/security/http.py +43 -0
  229. ouncemcp-1.0.0rc1/src/ouncemcp/security/identity.py +108 -0
  230. ouncemcp-1.0.0rc1/src/ouncemcp/security/passthrough.py +86 -0
  231. ouncemcp-1.0.0rc1/src/ouncemcp/security/policy.py +181 -0
  232. ouncemcp-1.0.0rc1/src/ouncemcp/security/replay.py +34 -0
  233. ouncemcp-1.0.0rc1/src/ouncemcp/security/state.py +184 -0
  234. ouncemcp-1.0.0rc1/src/ouncemcp/security/stdio.py +93 -0
  235. ouncemcp-1.0.0rc1/src/ouncemcp/security/urls.py +147 -0
  236. ouncemcp-1.0.0rc1/src/ouncemcp/testing.py +59 -0
  237. ouncemcp-1.0.0rc1/src/ouncemcp/trace/__init__.py +237 -0
  238. ouncemcp-1.0.0rc1/src/ouncemcp/trace/metrics.py +113 -0
  239. ouncemcp-1.0.0rc1/src/ouncemcp/trace/otel.py +77 -0
  240. ouncemcp-1.0.0rc1/src/ouncemcp/trace/report.py +97 -0
  241. ouncemcp-1.0.0rc1/src/ouncemcp/ui/__init__.py +5 -0
  242. ouncemcp-1.0.0rc1/src/ouncemcp/ui/page.py +69 -0
  243. ouncemcp-1.0.0rc1/src/ouncemcp/ui/static/THIRD_PARTY.md +88 -0
  244. ouncemcp-1.0.0rc1/src/ouncemcp/ui/static/report.html +70 -0
  245. ouncemcp-1.0.0rc1/src/ouncemcp/ui/views.py +94 -0
  246. ouncemcp-1.0.0rc1/tests/api_snapshot.json +840 -0
  247. ouncemcp-1.0.0rc1/tests/conftest.py +23 -0
  248. ouncemcp-1.0.0rc1/tests/test_api_surface.py +102 -0
  249. ouncemcp-1.0.0rc1/tests/test_audit.py +179 -0
  250. ouncemcp-1.0.0rc1/tests/test_bench.py +241 -0
  251. ouncemcp-1.0.0rc1/tests/test_book_shelf.py +196 -0
  252. ouncemcp-1.0.0rc1/tests/test_bud4_overrides.py +105 -0
  253. ouncemcp-1.0.0rc1/tests/test_bud5_traces.py +172 -0
  254. ouncemcp-1.0.0rc1/tests/test_bud6_tokenizer.py +306 -0
  255. ouncemcp-1.0.0rc1/tests/test_bud7_9_output_schemas.py +294 -0
  256. ouncemcp-1.0.0rc1/tests/test_budget_tokens.py +48 -0
  257. ouncemcp-1.0.0rc1/tests/test_cap2_resources_prompts.py +156 -0
  258. ouncemcp-1.0.0rc1/tests/test_cap7_composite.py +119 -0
  259. ouncemcp-1.0.0rc1/tests/test_cli.py +32 -0
  260. ouncemcp-1.0.0rc1/tests/test_client_broker.py +212 -0
  261. ouncemcp-1.0.0rc1/tests/test_costs.py +35 -0
  262. ouncemcp-1.0.0rc1/tests/test_disc_discovery.py +785 -0
  263. ouncemcp-1.0.0rc1/tests/test_dx1_dx2_init_dev.py +149 -0
  264. ouncemcp-1.0.0rc1/tests/test_dx3_schema_lints.py +143 -0
  265. ouncemcp-1.0.0rc1/tests/test_dx5_trace_viewer.py +128 -0
  266. ouncemcp-1.0.0rc1/tests/test_dx6_doc_links.py +83 -0
  267. ouncemcp-1.0.0rc1/tests/test_dx7_wrap.py +81 -0
  268. ouncemcp-1.0.0rc1/tests/test_err_errors.py +483 -0
  269. ouncemcp-1.0.0rc1/tests/test_eval1_conformance_fixtures.py +127 -0
  270. ouncemcp-1.0.0rc1/tests/test_eval2_security_catalog.py +172 -0
  271. ouncemcp-1.0.0rc1/tests/test_eval_agent_suites.py +188 -0
  272. ouncemcp-1.0.0rc1/tests/test_eval_openrouter.py +163 -0
  273. ouncemcp-1.0.0rc1/tests/test_exe4_idempotency.py +311 -0
  274. ouncemcp-1.0.0rc1/tests/test_exe5_parallel_safe.py +76 -0
  275. ouncemcp-1.0.0rc1/tests/test_exe_execution.py +778 -0
  276. ouncemcp-1.0.0rc1/tests/test_jobs_resume.py +168 -0
  277. ouncemcp-1.0.0rc1/tests/test_lints.py +107 -0
  278. ouncemcp-1.0.0rc1/tests/test_m2_ops.py +160 -0
  279. ouncemcp-1.0.0rc1/tests/test_package.py +28 -0
  280. ouncemcp-1.0.0rc1/tests/test_pilot_markitdown.py +121 -0
  281. ouncemcp-1.0.0rc1/tests/test_pilot_ticketing.py +126 -0
  282. ouncemcp-1.0.0rc1/tests/test_provider_tokens.py +136 -0
  283. ouncemcp-1.0.0rc1/tests/test_res11_handler_hints.py +84 -0
  284. ouncemcp-1.0.0rc1/tests/test_res5_handles_jobs.py +161 -0
  285. ouncemcp-1.0.0rc1/tests/test_res8_conformance.py +205 -0
  286. ouncemcp-1.0.0rc1/tests/test_res9_pagination.py +143 -0
  287. ouncemcp-1.0.0rc1/tests/test_safe_writes.py +210 -0
  288. ouncemcp-1.0.0rc1/tests/test_schema.py +34 -0
  289. ouncemcp-1.0.0rc1/tests/test_sec10_oauth.py +139 -0
  290. ouncemcp-1.0.0rc1/tests/test_sec12_cursors.py +458 -0
  291. ouncemcp-1.0.0rc1/tests/test_sec9_fetch.py +173 -0
  292. ouncemcp-1.0.0rc1/tests/test_sec9_urls.py +53 -0
  293. ouncemcp-1.0.0rc1/tests/test_sec_security.py +568 -0
  294. ouncemcp-1.0.0rc1/tests/test_server.py +149 -0
  295. ouncemcp-1.0.0rc1/tests/test_shaping.py +76 -0
  296. ouncemcp-1.0.0rc1/tests/test_shaping_fit.py +237 -0
  297. ouncemcp-1.0.0rc1/tests/test_stdio_e2e.py +44 -0
  298. ouncemcp-1.0.0rc1/tests/test_stdio_policy.py +123 -0
  299. ouncemcp-1.0.0rc1/tests/test_ui_report.py +163 -0
  300. ouncemcp-1.0.0rc1/ui/report/.gitignore +1 -0
  301. ouncemcp-1.0.0rc1/ui/report/build.sh +11 -0
  302. ouncemcp-1.0.0rc1/ui/report/index.html +14 -0
  303. ouncemcp-1.0.0rc1/ui/report/licenses.mjs +33 -0
  304. ouncemcp-1.0.0rc1/ui/report/package-lock.json +6591 -0
  305. ouncemcp-1.0.0rc1/ui/report/package.json +37 -0
  306. ouncemcp-1.0.0rc1/ui/report/src/App.tsx +53 -0
  307. ouncemcp-1.0.0rc1/ui/report/src/components/cost-bar.tsx +51 -0
  308. ouncemcp-1.0.0rc1/ui/report/src/components/stat-card.tsx +40 -0
  309. ouncemcp-1.0.0rc1/ui/report/src/components/theme-toggle.tsx +51 -0
  310. ouncemcp-1.0.0rc1/ui/report/src/components/ui/badge.tsx +47 -0
  311. ouncemcp-1.0.0rc1/ui/report/src/components/ui/button.tsx +63 -0
  312. ouncemcp-1.0.0rc1/ui/report/src/components/ui/card.tsx +91 -0
  313. ouncemcp-1.0.0rc1/ui/report/src/components/ui/input.tsx +20 -0
  314. ouncemcp-1.0.0rc1/ui/report/src/components/ui/progress.tsx +30 -0
  315. ouncemcp-1.0.0rc1/ui/report/src/components/ui/select.tsx +189 -0
  316. ouncemcp-1.0.0rc1/ui/report/src/components/ui/separator.tsx +27 -0
  317. ouncemcp-1.0.0rc1/ui/report/src/components/ui/table.tsx +115 -0
  318. ouncemcp-1.0.0rc1/ui/report/src/components/ui/tabs.tsx +90 -0
  319. ouncemcp-1.0.0rc1/ui/report/src/components/ui/tooltip.tsx +56 -0
  320. ouncemcp-1.0.0rc1/ui/report/src/index.css +91 -0
  321. ouncemcp-1.0.0rc1/ui/report/src/lib/format.ts +17 -0
  322. ouncemcp-1.0.0rc1/ui/report/src/lib/utils.ts +6 -0
  323. ouncemcp-1.0.0rc1/ui/report/src/main.tsx +31 -0
  324. ouncemcp-1.0.0rc1/ui/report/src/reports/audit-report.tsx +196 -0
  325. ouncemcp-1.0.0rc1/ui/report/src/reports/bench-report.tsx +259 -0
  326. ouncemcp-1.0.0rc1/ui/report/src/reports/context-report.tsx +210 -0
  327. ouncemcp-1.0.0rc1/ui/report/src/reports/eval-report.tsx +259 -0
  328. ouncemcp-1.0.0rc1/ui/report/src/reports/trace-report.tsx +444 -0
  329. ouncemcp-1.0.0rc1/ui/report/src/sample.ts +45 -0
  330. ouncemcp-1.0.0rc1/ui/report/src/types.ts +172 -0
  331. ouncemcp-1.0.0rc1/ui/report/tsconfig.json +17 -0
  332. ouncemcp-1.0.0rc1/ui/report/vite.config.ts +17 -0
@@ -0,0 +1,16 @@
1
+ __pycache__/
2
+ *.py[cod]
3
+ *.egg-info/
4
+ .venv/
5
+ venv/
6
+ build/
7
+ dist/
8
+ .pytest_cache/
9
+ .ruff_cache/
10
+ .mypy_cache/
11
+ .coverage
12
+ htmlcov/
13
+ .env
14
+ ouncemcp-report.*
15
+ site/
16
+ .hypothesis/
@@ -0,0 +1,39 @@
1
+ # Changelog
2
+
3
+ All notable changes to OunceMCP. The format follows [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and versions follow [semantic versioning](https://semver.org/) with the promises in [proposal 0029](https://github.com/subbusainath/ouncemcp/blob/main/docs/proposals/0029-api-freeze.md).
4
+
5
+ ## 1.0.0rc1 (15 November 2026)
6
+
7
+ Release candidate for 1.0.0. Install it with `pip install --pre ouncemcp` or `pipx install "ouncemcp==1.0.0rc1"`. 1.0.0 follows the external security review; the API below will not change before it unless the review requires it.
8
+
9
+ The first stable release. The public API is frozen: names listed as stable in proposal 0029 do not change in a breaking way before 2.0.
10
+
11
+ ### Stable
12
+
13
+ - **Server and tools** (`ouncemcp`): `OunceServer` with typed tools, declared effects, resources, prompts and background jobs; `ToolContext`; budgets and per-tool overrides; response shaping with `Detail`, `Paginated` and signed `Cursor`s; automatic discovery; errors in eight categories; deadlines, retries and limits; stores, idempotent writes and approvals; handles and resumable jobs; tokenizers and cost reports.
14
+ - **Security** (`ouncemcp.security`): verified identity, policies, partitioned caches, signed state, safe outbound fetches and token-passthrough guards.
15
+ - **Clients** (`ouncemcp.client`): the `Broker` over many servers and `OunceOAuth`.
16
+ - **Traces** (`ouncemcp.trace`): the tracer, its sinks and redaction.
17
+ - **Testing** (`ouncemcp.testing`) and the **security suite** (`ouncemcp.eval`).
18
+ - **Contracts outside Python:** error categories and codes, `_meta["ouncemcp"]` keys, the `handles.read` and `jobs.get` tools, command names, flags and exit codes, report `report_version`, and lint rule names.
19
+
20
+ ### Provisional
21
+
22
+ May change in a minor release, with the change named here: `ouncemcp.eval.agent` (task suites and models), `ouncemcp.eval.bench`, `ouncemcp.ui` and `AnthropicCounter`.
23
+
24
+ ### Added
25
+
26
+ - `AnthropicCounter` and `--tokenizer anthropic:MODEL`: token counts from Anthropic's counting API, through the official SDK (`pip install "ouncemcp[anthropic]"`). Provisional (spec BUD-6).
27
+
28
+ ### Deprecated
29
+
30
+ - `ouncemcp.security.resolve_http_options` and `ouncemcp.security.partition_key` are internal. They still work but raise a `DeprecationWarning`, and will be removed in 1.1. `OunceServer.run` and `PartitionedCache` use them for you.
31
+
32
+ ### Changed
33
+
34
+ - FastMCP is supported as a range, `>=4.1.6,<5`, instead of an exact pin. CI tests the lowest version (the lockfile) and the newest release in the range.
35
+
36
+ ### Supported versions
37
+
38
+ - Python 3.11, 3.12 and 3.13.
39
+ - FastMCP 4.1.6 and later 4.x releases.
@@ -0,0 +1,32 @@
1
+ # Contributing to OunceMCP
2
+
3
+ OunceMCP is pre-alpha. The best contributions right now are feedback on the [specification](docs/spec.md) through issues.
4
+
5
+ ## Good first contributions
6
+
7
+ These are designed to be small and self-contained:
8
+
9
+ - New lint rules for tool definitions, each with a fixture that triggers it and a documentation page.
10
+ - New benchmark servers or task suites with verifiable outcomes.
11
+ - Documentation fixes.
12
+
13
+ ## Development
14
+
15
+ ```bash
16
+ uv venv && uv pip install -e ".[dev]"
17
+ uv run pytest
18
+ uv run ruff check .
19
+ ```
20
+
21
+ ## Pull requests
22
+
23
+ - Keep changes focused and include tests.
24
+ - Changes to the public API or default behavior need a design proposal in `docs/proposals/`.
25
+ - Any change that affects token usage should state its measured effect.
26
+
27
+ By participating you agree to the [Code of Conduct](CODE_OF_CONDUCT.md).
28
+
29
+ ## Public API changes
30
+
31
+ `tests/test_api_surface.py` records every exported name and public signature of `ouncemcp`, `ouncemcp.security`, `ouncemcp.client`, `ouncemcp.eval.agent`, `ouncemcp.testing` and `ouncemcp.ui` in `tests/api_snapshot.json`. A change to the public API fails it on purpose. If the change is intended, regenerate the snapshot with `OUNCEMCP_UPDATE_API_SNAPSHOT=1 uv run pytest tests/test_api_surface.py` and explain the change in the PR. After 1.0, removing or changing a public name needs a deprecation first.
32
+
@@ -0,0 +1,202 @@
1
+ Apache License
2
+ Version 2.0, January 2004
3
+ http://www.apache.org/licenses/
4
+
5
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6
+
7
+ 1. Definitions.
8
+
9
+ "License" shall mean the terms and conditions for use, reproduction,
10
+ and distribution as defined by Sections 1 through 9 of this document.
11
+
12
+ "Licensor" shall mean the copyright owner or entity authorized by
13
+ the copyright owner that is granting the License.
14
+
15
+ "Legal Entity" shall mean the union of the acting entity and all
16
+ other entities that control, are controlled by, or are under common
17
+ control with that entity. For the purposes of this definition,
18
+ "control" means (i) the power, direct or indirect, to cause the
19
+ direction or management of such entity, whether by contract or
20
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
21
+ outstanding shares, or (iii) beneficial ownership of such entity.
22
+
23
+ "You" (or "Your") shall mean an individual or Legal Entity
24
+ exercising permissions granted by this License.
25
+
26
+ "Source" form shall mean the preferred form for making modifications,
27
+ including but not limited to software source code, documentation
28
+ source, and configuration files.
29
+
30
+ "Object" form shall mean any form resulting from mechanical
31
+ transformation or translation of a Source form, including but
32
+ not limited to compiled object code, generated documentation,
33
+ and conversions to other media types.
34
+
35
+ "Work" shall mean the work of authorship, whether in Source or
36
+ Object form, made available under the License, as indicated by a
37
+ copyright notice that is included in or attached to the work
38
+ (an example is provided in the Appendix below).
39
+
40
+ "Derivative Works" shall mean any work, whether in Source or Object
41
+ form, that is based on (or derived from) the Work and for which the
42
+ editorial revisions, annotations, elaborations, or other modifications
43
+ represent, as a whole, an original work of authorship. For the purposes
44
+ of this License, Derivative Works shall not include works that remain
45
+ separable from, or merely link (or bind by name) to the interfaces of,
46
+ the Work and Derivative Works thereof.
47
+
48
+ "Contribution" shall mean any work of authorship, including
49
+ the original version of the Work and any modifications or additions
50
+ to that Work or Derivative Works thereof, that is intentionally
51
+ submitted to Licensor for inclusion in the Work by the copyright owner
52
+ or by an individual or Legal Entity authorized to submit on behalf of
53
+ the copyright owner. For the purposes of this definition, "submitted"
54
+ means any form of electronic, verbal, or written communication sent
55
+ to the Licensor or its representatives, including but not limited to
56
+ communication on electronic mailing lists, source code control systems,
57
+ and issue tracking systems that are managed by, or on behalf of, the
58
+ Licensor for the purpose of discussing and improving the Work, but
59
+ excluding communication that is conspicuously marked or otherwise
60
+ designated in writing by the copyright owner as "Not a Contribution."
61
+
62
+ "Contributor" shall mean Licensor and any individual or Legal Entity
63
+ on behalf of whom a Contribution has been received by Licensor and
64
+ subsequently incorporated within the Work.
65
+
66
+ 2. Grant of Copyright License. Subject to the terms and conditions of
67
+ this License, each Contributor hereby grants to You a perpetual,
68
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69
+ copyright license to reproduce, prepare Derivative Works of,
70
+ publicly display, publicly perform, sublicense, and distribute the
71
+ Work and such Derivative Works in Source or Object form.
72
+
73
+ 3. Grant of Patent License. Subject to the terms and conditions of
74
+ this License, each Contributor hereby grants to You a perpetual,
75
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76
+ (except as stated in this section) patent license to make, have made,
77
+ use, offer to sell, sell, import, and otherwise transfer the Work,
78
+ where such license applies only to those patent claims licensable
79
+ by such Contributor that are necessarily infringed by their
80
+ Contribution(s) alone or by combination of their Contribution(s)
81
+ with the Work to which such Contribution(s) was submitted. If You
82
+ institute patent litigation against any entity (including a
83
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
84
+ or a Contribution incorporated within the Work constitutes direct
85
+ or contributory patent infringement, then any patent licenses
86
+ granted to You under this License for that Work shall terminate
87
+ as of the date such litigation is filed.
88
+
89
+ 4. Redistribution. You may reproduce and distribute copies of the
90
+ Work or Derivative Works thereof in any medium, with or without
91
+ modifications, and in Source or Object form, provided that You
92
+ meet the following conditions:
93
+
94
+ (a) You must give any other recipients of the Work or
95
+ Derivative Works a copy of this License; and
96
+
97
+ (b) You must cause any modified files to carry prominent notices
98
+ stating that You changed the files; and
99
+
100
+ (c) You must retain, in the Source form of any Derivative Works
101
+ that You distribute, all copyright, patent, trademark, and
102
+ attribution notices from the Source form of the Work,
103
+ excluding those notices that do not pertain to any part of
104
+ the Derivative Works; and
105
+
106
+ (d) If the Work includes a "NOTICE" text file as part of its
107
+ distribution, then any Derivative Works that You distribute must
108
+ include a readable copy of the attribution notices contained
109
+ within such NOTICE file, excluding those notices that do not
110
+ pertain to any part of the Derivative Works, in at least one
111
+ of the following places: within a NOTICE text file distributed
112
+ as part of the Derivative Works; within the Source form or
113
+ documentation, if provided along with the Derivative Works; or,
114
+ within a display generated by the Derivative Works, if and
115
+ wherever such third-party notices normally appear. The contents
116
+ of the NOTICE file are for informational purposes only and
117
+ do not modify the License. You may add Your own attribution
118
+ notices within Derivative Works that You distribute, alongside
119
+ or as an addendum to the NOTICE text from the Work, provided
120
+ that such additional attribution notices cannot be construed
121
+ as modifying the License.
122
+
123
+ You may add Your own copyright statement to Your modifications and
124
+ may provide additional or different license terms and conditions
125
+ for use, reproduction, or distribution of Your modifications, or
126
+ for any such Derivative Works as a whole, provided Your use,
127
+ reproduction, and distribution of the Work otherwise complies with
128
+ the conditions stated in this License.
129
+
130
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
131
+ any Contribution intentionally submitted for inclusion in the Work
132
+ by You to the Licensor shall be under the terms and conditions of
133
+ this License, without any additional terms or conditions.
134
+ Notwithstanding the above, nothing herein shall supersede or modify
135
+ the terms of any separate license agreement you may have executed
136
+ with Licensor regarding such Contributions.
137
+
138
+ 6. Trademarks. This License does not grant permission to use the trade
139
+ names, trademarks, service marks, or product names of the Licensor,
140
+ except as required for reasonable and customary use in describing the
141
+ origin of the Work and reproducing the content of the NOTICE file.
142
+
143
+ 7. Disclaimer of Warranty. Unless required by applicable law or
144
+ agreed to in writing, Licensor provides the Work (and each
145
+ Contributor provides its Contributions) on an "AS IS" BASIS,
146
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147
+ implied, including, without limitation, any warranties or conditions
148
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149
+ PARTICULAR PURPOSE. You are solely responsible for determining the
150
+ appropriateness of using or redistributing the Work and assume any
151
+ risks associated with Your exercise of permissions under this License.
152
+
153
+ 8. Limitation of Liability. In no event and under no legal theory,
154
+ whether in tort (including negligence), contract, or otherwise,
155
+ unless required by applicable law (such as deliberate and grossly
156
+ negligent acts) or agreed to in writing, shall any Contributor be
157
+ liable to You for damages, including any direct, indirect, special,
158
+ incidental, or consequential damages of any character arising as a
159
+ result of this License or out of the use or inability to use the
160
+ Work (including but not limited to damages for loss of goodwill,
161
+ work stoppage, computer failure or malfunction, or any and all
162
+ other commercial damages or losses), even if such Contributor
163
+ has been advised of the possibility of such damages.
164
+
165
+ 9. Accepting Warranty or Additional Liability. While redistributing
166
+ the Work or Derivative Works thereof, You may choose to offer,
167
+ and charge a fee for, acceptance of support, warranty, indemnity,
168
+ or other liability obligations and/or rights consistent with this
169
+ License. However, in accepting such obligations, You may act only
170
+ on Your own behalf and on Your sole responsibility, not on behalf
171
+ of any other Contributor, and only if You agree to indemnify,
172
+ defend, and hold each Contributor harmless for any liability
173
+ incurred by, or claims asserted against, such Contributor by reason
174
+ of your accepting any such warranty or additional liability.
175
+
176
+ END OF TERMS AND CONDITIONS
177
+
178
+ APPENDIX: How to apply the Apache License to your work.
179
+
180
+ To apply the Apache License to your work, attach the following
181
+ boilerplate notice, with the fields enclosed by brackets "[]"
182
+ replaced with your own identifying information. (Don't include
183
+ the brackets!) The text should be enclosed in the appropriate
184
+ comment syntax for the file format. We also recommend that a
185
+ file or class name and description of purpose be included on the
186
+ same "printed page" as the copyright notice for easier
187
+ identification within third-party archives.
188
+
189
+ Copyright [yyyy] [name of copyright owner]
190
+
191
+ Licensed under the Apache License, Version 2.0 (the "License");
192
+ you may not use this file except in compliance with the License.
193
+ You may obtain a copy of the License at
194
+
195
+ http://www.apache.org/licenses/LICENSE-2.0
196
+
197
+ Unless required by applicable law or agreed to in writing, software
198
+ distributed under the License is distributed on an "AS IS" BASIS,
199
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200
+ See the License for the specific language governing permissions and
201
+ limitations under the License.
202
+
@@ -0,0 +1,232 @@
1
+ Metadata-Version: 2.5
2
+ Name: ouncemcp
3
+ Version: 1.0.0rc1
4
+ Summary: Build MCP servers and clients that are fast, low-noise, and safe for agents by default.
5
+ Project-URL: Homepage, https://subbusainath.github.io/ouncemcp/
6
+ Project-URL: Documentation, https://subbusainath.github.io/ouncemcp/
7
+ Project-URL: Repository, https://github.com/subbusainath/ouncemcp
8
+ Project-URL: Changelog, https://github.com/subbusainath/ouncemcp/blob/main/CHANGELOG.md
9
+ Project-URL: Issues, https://github.com/subbusainath/ouncemcp/issues
10
+ Author: Subbusainath Rengasamy
11
+ License-Expression: Apache-2.0
12
+ License-File: LICENSE
13
+ Keywords: agents,fastmcp,llm,mcp,model-context-protocol
14
+ Classifier: Development Status :: 4 - Beta
15
+ Classifier: Intended Audience :: Developers
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Programming Language :: Python :: 3.11
18
+ Classifier: Programming Language :: Python :: 3.12
19
+ Classifier: Programming Language :: Python :: 3.13
20
+ Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
21
+ Requires-Python: >=3.11
22
+ Requires-Dist: fastmcp<5,>=4.1.6
23
+ Requires-Dist: jsonschema>=4.20
24
+ Requires-Dist: pydantic<3,>=2.11
25
+ Requires-Dist: tiktoken==0.14.0
26
+ Provides-Extra: anthropic
27
+ Requires-Dist: anthropic<3,>=2.3; extra == 'anthropic'
28
+ Provides-Extra: client
29
+ Provides-Extra: dev
30
+ Requires-Dist: anthropic<3,>=2.3; extra == 'dev'
31
+ Requires-Dist: anyio>=4; extra == 'dev'
32
+ Requires-Dist: hypothesis>=6.100; extra == 'dev'
33
+ Requires-Dist: mypy>=1.10; extra == 'dev'
34
+ Requires-Dist: opentelemetry-sdk>=1.28; extra == 'dev'
35
+ Requires-Dist: pytest>=8; extra == 'dev'
36
+ Requires-Dist: pyyaml>=6; extra == 'dev'
37
+ Requires-Dist: ruff>=0.6; extra == 'dev'
38
+ Requires-Dist: types-pyyaml; extra == 'dev'
39
+ Provides-Extra: docs
40
+ Requires-Dist: mkdocs-material==9.8.1; extra == 'docs'
41
+ Requires-Dist: mkdocs==1.6.1; extra == 'docs'
42
+ Requires-Dist: mkdocstrings-python==2.0.11; extra == 'docs'
43
+ Requires-Dist: mkdocstrings==1.0.8; extra == 'docs'
44
+ Provides-Extra: eval
45
+ Requires-Dist: pyyaml>=6; extra == 'eval'
46
+ Provides-Extra: otel
47
+ Requires-Dist: opentelemetry-api>=1.28; extra == 'otel'
48
+ Provides-Extra: ui
49
+ Description-Content-Type: text/markdown
50
+
51
+ # OunceMCP
52
+
53
+ **Every token counts.** OunceMCP is a framework for building Model Context Protocol (MCP) servers and clients that are fast, low-noise, and safe for agents by default. It builds on [FastMCP](https://gofastmcp.com) and the official MCP Python SDK, and targets the MCP `2026-07-28` specification.
54
+
55
+ > **Status: pre-1.0 (M0 to M4 built).** The framework, safe writes, jobs, the client broker, the eval kit, the trace viewer and the benchmark work and are tested; the official MCP conformance suite passes in CI and the M4 performance gates are met ([M4 report](docs/m4-report.md)). The 1.0 API is frozen ([proposal 0029](docs/proposals/0029-api-freeze.md), [changelog](CHANGELOG.md)); 1.0 ships after the external security review. Release gates that need people or outside systems are listed in the [spec](docs/spec.md) (section 14.2). New here? Read [The OunceMCP Book](docs/book/index.md).
56
+
57
+ ## Why
58
+
59
+ Most MCP slowdowns don't come from the protocol. They come from loading every tool definition up front, returning large raw results, and making the model chain many small calls. OunceMCP makes the efficient design the default one:
60
+
61
+ - **Context budgets:** token limits for tool definitions, the startup catalog, and each result, checked in CI.
62
+ - **Automatic discovery:** switches from exposing every tool to search-then-call once the catalog is too large.
63
+ - **Response shaping:** concise results by default, field selection, pagination, truncation notes, and handles for large data.
64
+ - **Safe execution:** declared effects, deadlines, bounded retries, idempotent writes, and approvals.
65
+ - **Visibility:** see exactly what the model received and what it cost.
66
+ - **Optional audit:** measure any MCP server, including ones not built with OunceMCP.
67
+
68
+ ## Quickstart
69
+
70
+ ```bash
71
+ pipx install "ouncemcp==1.0.0rc1" # or: uv tool install --prerelease allow ouncemcp
72
+ ouncemcp init notes-server && cd notes-server
73
+ uv venv && uv pip install --prerelease allow -e ".[dev]"
74
+ uv run pytest && uv run ouncemcp dev server.py
75
+ ```
76
+
77
+ The full walkthrough is in [docs/quickstart.md](docs/quickstart.md). To learn the framework step by step, read [The OunceMCP Book](docs/book/index.md): one library server built across 15 chapters, every stage tested. A tool looks like this:
78
+
79
+ ```python
80
+ from typing import Annotated
81
+
82
+ from pydantic import BaseModel, Field
83
+
84
+ from ouncemcp import Cursor, Detail, Effect, OunceServer, Paginated, ToolContext
85
+
86
+ server = OunceServer("inventory")
87
+
88
+
89
+ class SearchInput(BaseModel):
90
+ region: str = Field(description="Region, e.g. ap-south-1")
91
+ limit: int = Field(20, ge=1, le=50)
92
+ cursor: str | None = None
93
+
94
+
95
+ class Resource(BaseModel):
96
+ id: str
97
+ name: str
98
+ status: str
99
+ tags: Annotated[dict[str, str] | None, Detail] = None # only with response_format=detailed
100
+
101
+
102
+ class SearchResult(BaseModel):
103
+ items: Annotated[list[Resource], Paginated]
104
+ next_cursor: Cursor = None
105
+
106
+
107
+ @server.tool(name="inventory.search", effect=Effect.READ, deadline_ms=5_000)
108
+ async def search(args: SearchInput, ctx: ToolContext) -> SearchResult:
109
+ """Find cloud resources in a region."""
110
+ rows = await load_resources(args.region)
111
+ page = ctx.page(rows, cursor=args.cursor, limit=args.limit)
112
+ return SearchResult(items=page.items, next_cursor=page.next_cursor)
113
+
114
+
115
+ if __name__ == "__main__":
116
+ server.run() # stdio; prints a token budget summary to stderr
117
+ ```
118
+
119
+ What you get:
120
+
121
+ - **Declared effects:** `effect` is required and maps to MCP `readOnlyHint` and `destructiveHint`.
122
+ - **Typed inputs and outputs:** Pydantic models in, Pydantic models out, validated both ways.
123
+ - **Compact schemas:** optional fields are advertised without the `anyOf`/`null` noise.
124
+ - **Concise by default:** fields marked `Detail` are only returned when the agent asks for `response_format="detailed"`.
125
+ - **Result budgets:** every result fits its token budget and still matches its output schema. Lists are trimmed first, then long strings, then optional fields, and the agent gets a note saying what was removed and how to get more. If the smallest valid result is still too large, the agent gets an `isError` result asking for a narrower call instead of broken JSON.
126
+ - **Safe pagination:** mark the page list `Paginated` and the cursor `Cursor`, and use `ctx.page(...)`. If OunceMCP trims the page to fit the budget, it moves `next_cursor` so the next call resumes after the last item returned. No item is skipped or repeated. Lists are never trimmed below `min_items` (default 1). Cursors are signed: they expire, and only work for the same tool, caller and filters. Set `OUNCEMCP_CURSOR_KEYS` in production ([proposal 0003](docs/proposals/0003-signed-cursors.md)).
127
+ - **Automatic discovery:** `OunceServer(discovery="auto")` (the default) sends every tool while the definitions fit `Budget.startup_tokens`. Above it, the server sends `search_tools`, `describe_tool` and `call_tool` instead (279 tokens whatever the catalog), plus any `pinned` tools. Search results come in three detail levels (`name`, `summary`, `full`) and only include tools the caller is authorized to use (`@server.tool(auth=...)`). Hiding a tool is not access control: the same check runs on a guessed name. `discovery="direct"` or `"search"` overrides the choice, and `await server.measure_recall(cases)` reports how often search finds the tools agents need. See [proposal 0006](docs/proposals/0006-discovery-modes.md).
128
+ - **Categorized errors:** every failure is one of validation, authentication, permission, not found, rate limit, transient, ambiguous write or internal, with a `retryable` flag. Validation messages say how to fix the input without repeating it. Unexpected exceptions are never shown to the model: it gets a reference id and the details go to the log. Messages you write are redacted for URLs, addresses, hostnames, paths and credentials. Raise `InvalidInput`, `NotFound`, `RateLimited` and the other `OunceError` classes from handlers.
129
+ - **Execution limits:** every call has a deadline (default 30 s) and is cancelled when it passes or the client cancels. Reads are retried a few times with bounded backoff inside the deadline, and writes never are. A write that times out is reported as ambiguous, not as failed. Request size, nesting depth and calls in flight per principal are bounded (`Limits`). See [proposal 0007](docs/proposals/0007-errors-and-execution-limits.md).
130
+ - **Security and policy:** `ctx.identity` comes from the verified token only. `policy=` on the server or a tool decides who sees and calls it, on the declared effect, never on annotations. `audience=` refuses tokens issued for other servers, `passthrough_guard()` stops the caller's token from reaching upstream APIs, `ctx.cache` is partitioned by tenant, `ctx.issue_state` hands out handles and approvals bound to caller, request and expiry, and HTTP binds to loopback with `Host` and `Origin` checks ([proposal 0008](docs/proposals/0008-security-and-policy.md)).
131
+ - **Safe writes:** `idempotency=True` runs a write once per caller and arguments, and a write that times out finishes in the background so a retry gets its result instead of writing twice. `approval="required"` with a `preview` asks a person first, through the client, and refuses when nobody can be asked ([safe writes](docs/guides/safe-writes.md)).
132
+ - **Large results and slow work:** `OunceServer(handles=True)` adds `handles.read` and `jobs.get`. `await ctx.store_result(rows)` returns a handle to page, and `await ctx.start_job(work)` runs work in the background. Both work across replicas with a shared `RedisStore`.
133
+ - **Resources and prompts:** `@server.resource(uri)` and `@server.prompt()` get the same budgets, policies and verified caller as tools.
134
+ - **Migration and audit:** `OunceServer.wrap(existing_fastmcp_server)` adds all of the above without touching its tools, and `ouncemcp audit <url | file | -- command>` measures any MCP server ([migration guide](docs/guides/migrating-from-fastmcp.md)).
135
+ - **Many servers:** `ouncemcp.client.Broker` connects a host to many servers as one catalog, with namespaced tools, cross-server search, schema checks, deadlines, failure isolation, and no cross-server authority from server output ([broker](docs/guides/client-broker.md)).
136
+ - **Operations:** `OunceServer.from_config(name, "ouncemcp.toml")`, `/healthz`, `/readyz` and, with `metrics=True`, Prometheus `/metrics` ([deploying](docs/guides/deploying-http.md)).
137
+ - **Traces:** `OunceServer(trace=MemorySink())` records each call's result tokens, latency, retries and outcome, redacted by default ([proposal 0009](docs/proposals/0009-traces.md)). `ouncemcp trace calls.jsonl` writes an HTML report you can search and filter ([proposal 0020](docs/proposals/0020-report-ui.md)), and `OTelSink()` exports spans to OpenTelemetry ([proposal 0018](docs/proposals/0018-trace-viewer.md)).
138
+ - **Task suites:** `ouncemcp eval suite.yaml` runs YAML tasks against a server, judges outcomes on the server, and reports success, tool choice, tokens, latency and regressions against a baseline ([proposal 0019](docs/proposals/0019-eval-kit.md)).
139
+ - **Composite tools:** `ctx.gather(...)` runs several upstream calls in one tool call. The example `inventory.cost_review` returns 651 tokens in 1 call where paging `inventory.search` takes 9,896 tokens over 8 calls.
140
+ - **Testing:** `ouncemcp.testing.as_caller(...)` runs code as a verified caller, and `ouncemcp.eval.run_security_suite(server, cases)` probes for cross-tenant leaks, guessed names, cursor and handle reuse, replay, token passthrough and SSRF.
141
+ - **Budget checks:** `ouncemcp check server.py` counts the tokens of every tool definition as the host receives it, and fails CI when a tool goes over budget or a lint error is found. It also reports which discovery mode the server will use and why. Every lint and error links to a page explaining it.
142
+
143
+ ```bash
144
+ ouncemcp check examples/inventory/server.py
145
+ ouncemcp context examples/inventory/server.py inventory.search
146
+ ouncemcp context examples/inventory/server.py --html context.html # every tool, as a page
147
+ ouncemcp dev examples/inventory/server.py # hot reload, live per-call token lines
148
+ ```
149
+
150
+ Counts use `o200k_base` by default. Choose another tiktoken encoding with `OunceServer(tokenizer="cl100k_base")` or `ouncemcp check --tokenizer cl100k_base`, or pass your own counting function with `tokenizer=fn, tokenizer_name="my-tokenizer-v1"`. Every report names the tokenizer it used ([proposal 0004](docs/proposals/0004-pluggable-tokenizer.md)).
151
+
152
+ `ouncemcp audit` is the optional command for measuring any server. `ouncemcp eval` runs task suites, with the replay model or real models through OpenRouter, `ouncemcp bench` compares servers across models, and `ouncemcp trace` reads traces.
153
+
154
+ ## Benchmark (M3)
155
+
156
+ Two pilot servers against their plain FastMCP equivalents: four models (Claude Haiku 5.5, Claude Sonnet 5.5, GPT-5.4 mini, DeepSeek V4.1 Flash), 21 tasks, 5 runs each, success judged on outcomes. See the [full results and caveats](benchmarks/results/m3-bench.md).
157
+
158
+ | | Inventory (large results) | Ticketing (writes, small results) |
159
+ | --- | --- | --- |
160
+ | Tokens per task, OunceMCP vs plain | 58–85% fewer | 17–40% more |
161
+ | Task success, OunceMCP vs plain | 100% vs 75–100% | 100% vs 68–70% |
162
+ | Tool definitions, OunceMCP vs plain | 584 vs 244 tokens | 649 vs 366 tokens |
163
+
164
+ On these small servers the definitions are larger, mostly from output schemas. PERF-6, revised for small servers (definitions at most 2× plain, no drop in success), is met for ticketing and missed for inventory at 2.4× ([proposal 0027](docs/proposals/0027-benchmark.md)). On the plain ticketing server every model skipped the approval, deleted without permission and read another team's ticket.
165
+
166
+ ## M0 results
167
+
168
+ Measured on the inventory example with the `o200k_base` tokenizer. Full method and caveats are in [docs/m0-report.md](docs/m0-report.md).
169
+
170
+ | | OunceMCP | Plain FastMCP |
171
+ | --- | ---: | ---: |
172
+ | Tool definitions (2 tools) | 488 tokens | 195 tokens |
173
+ | Four-task session, definitions plus results | 1,510 tokens | 32,280 tokens |
174
+ | Added server time per call, p50 / p95 | 0.14–0.44 ms / 0.07–0.47 ms (1.44 / 1.32 ms when truncating) | baseline |
175
+
176
+ In M1, results that are trimmed must still match the output schema. Truncated calls now add 0.70 / 0.80 ms (p50 / p95) and other calls 0.14 to 0.31 ms. Shaping a 36,000-token result down to a 2,000-token budget takes 7.3 ms. See [proposal 0002](docs/proposals/0002-schema-safe-shaping-and-pagination.md) and `benchmarks/scripts/m1_truncation.py`.
177
+
178
+ Typed output schemas make definitions larger. Results are where the savings come from. Output schemas can be set to `"on"` (default), `"off"` or `"auto"` for a server (`output_schemas=`) or one tool (`output_schema=`). In M1, `"off"` gave 247 tokens for this example, and `"auto"`, which drops a schema only where it would put a definition over the per-tool target, gave 378. `ouncemcp check` reports the output-schema share of definition tokens (241 of 504 in the current example) and states which fields it counts: `name`, `description`, `inputSchema` and `outputSchema`, not `title`, `annotations` or `_meta` ([proposal 0005](docs/proposals/0005-counting-scope-and-output-schema-modes.md)).
179
+
180
+ ## Repository layout
181
+
182
+ ```text
183
+ src/ouncemcp/
184
+ core/ capabilities, budgets, response shaping, token accounting, errors, lints,
185
+ resources and prompts, config file
186
+ security/ identity, policy, audience and passthrough, caches, bound state, HTTP
187
+ defaults, stdio policy, URL checks
188
+ discovery/ direct and search discovery modes
189
+ execution/ deadlines, retries, limits, middleware, store, idempotency, approvals,
190
+ handles and jobs
191
+ trace/ per-call traces, redaction and metrics
192
+ eval/ conformance target, security test catalog and agent task suites
193
+ cli/ init, dev, check, context, audit, eval, trace (bench planned)
194
+ client/ reference client broker
195
+ audit/ optional audit command
196
+ testing.py as_caller and make_token for tests
197
+ examples/ pilot servers: inventory, ticketing, report, markitdown migration
198
+ benchmarks/ servers, tasks, scripts, published results
199
+ docs/ specification, research, concepts, guides, proposals
200
+ tests/
201
+ ```
202
+
203
+ ## Roadmap
204
+
205
+ | Milestone | Scope | Status |
206
+ | --- | --- | --- |
207
+ | M0: core spike | `OunceServer`, typed tools with effects, token counting, result budgets, one example server | Built ([M0 report](docs/m0-report.md)) |
208
+ | M1: v0.1 | Framework core, automatic discovery, errors, security, `init`/`dev`/`check`/`context`, docs | Built |
209
+ | M2: v0.2 | Safe writes, client broker, migration guide, pilots, optional `audit` | Built ([M2 report](docs/m2-report.md)) |
210
+ | M3: v0.5 | Eval kit, trace viewer, public benchmark, launch | Built ([benchmark](benchmarks/results/m3-bench.md)); launch waits on the repository going public |
211
+ | M4: v1.0 | Security review, host compatibility matrix, stable API, the book | API frozen ([0029](docs/proposals/0029-api-freeze.md)), all release gates checked, release workflow ready ([M4 report](docs/m4-report.md)); waiting on the external security review |
212
+
213
+ The full plan is in [docs/spec.md](docs/spec.md), and the supporting research is in [docs/performance-research.md](docs/performance-research.md).
214
+
215
+ ## Development
216
+
217
+ ```bash
218
+ uv venv && uv pip install -e ".[dev]"
219
+ uv run pytest
220
+ uv run ruff check .
221
+ uv pip install -e ".[docs]" && uv run mkdocs serve # documentation site
222
+ python benchmarks/scripts/m0_compare.py # token comparison
223
+ python benchmarks/scripts/m0_overhead.py # per-call overhead
224
+ ```
225
+
226
+ ## Contributing
227
+
228
+ See [CONTRIBUTING.md](CONTRIBUTING.md). Security issues: see [SECURITY.md](SECURITY.md).
229
+
230
+ ## License
231
+
232
+ [Apache-2.0](LICENSE)