hyperforge 1.0.0.post437__tar.gz → 1.0.0.post447__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.
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/PKG-INFO +26 -1
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/README.md +24 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/pyproject.toml +2 -1
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/pyproject.toml.orig +2 -1
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/app.py +4 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/mcp_server_pool.py +91 -55
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/settings.py +1 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/mcp_interaction.py +29 -28
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/app.py +4 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/settings.py +1 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/VERSION +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/auth.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/card.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/context.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/executor.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/run.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/server.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/settings.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/a2a/task_store.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/agent.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/README +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/env.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/script.py.mako +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/049a4aed19e1_create_generation_table.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/1416cb41bd49_add_downloads_table.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/2847934e2d59_unique_driver.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/4d89a36c8bda_memory.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/6a23d8ef74c1_add_sync_oauth_credentials.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/72b997edb05d_identifier_for_drivers.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/8c7d2e4f91ab_structure_legacy_llm_fields.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/9c6f6a1b4e7f_workflows_default_fk.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/b8edb72295e0_.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic/versions/f3d2c1b0a9e8_soft_delete_workflows.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/alembic.ini +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/authentication.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/commands.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/logging.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/models.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/session.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/agents.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/interaction.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/mcp_content.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/oauth.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/prompt.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/router.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/schema.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/session.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/workflows.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/arag.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/broker/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/broker/local.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/broker/redis.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/codemode/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/codemode/model.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/codemode/sandbox.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/codemode/worker.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/completions.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/configure.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/context/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/context/agent.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/context/config.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/database.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/agents.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/encryption.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/exceptions.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/legacy_llm_migration.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/llm_migration_utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/settings.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/workflow_cleanup.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/definition.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/driver.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/dummy.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/engine.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/errors.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/exceptions.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/feature_flag.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/fixtures.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness/agent.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness/config.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/agents.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/clients.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/context.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/harness.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/models.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/schema.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/storage.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/codemode.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/core.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/usage.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/interaction.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/json_schema.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/llm.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/llm_config.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/manager.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/memory/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/memory/memory.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/minimal_fixtures.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/models.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/openapi.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/adapters.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/cli.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/evolution.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/graph.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/guidance.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/prompts.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/pubsub.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/py.typed +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/redis_utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/result_payload.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/retrieval/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/retrieval/agent.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/retrieval/config.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/cache.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/run.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/sandbox.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/session.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/settings.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/utils.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/server/web.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/settings.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/agent.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/config.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/const.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/oauth.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/run.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/ui_router.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/static/assets/index-DLejS5jC.js +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/static/assets/index-MoxkZ1_3.css +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/static/favicon.svg +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/static/index.html +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/trace.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/utils/__init__.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/utils/http.py +0 -0
- {hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/workflows.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: hyperforge
|
|
3
|
-
Version: 1.0.0.
|
|
3
|
+
Version: 1.0.0.post447
|
|
4
4
|
Summary: Agentic Framework for Orchestrated Runtime, Governance, and Execution
|
|
5
5
|
Author: AI Data Team
|
|
6
6
|
Author-email: AI Data Team <learning@nuclia.com>
|
|
@@ -23,6 +23,7 @@ Requires-Dist: aiodns
|
|
|
23
23
|
Requires-Dist: anyio>=4.15.1
|
|
24
24
|
Requires-Dist: httpx>=0.28.1,<0.29
|
|
25
25
|
Requires-Dist: httpcore>=1.0.9,<1.1
|
|
26
|
+
Requires-Dist: urllib3>=2.8.0,<3
|
|
26
27
|
Requires-Dist: jinja2
|
|
27
28
|
Requires-Dist: jsonschema
|
|
28
29
|
Requires-Dist: restrictedpython>=8.5
|
|
@@ -132,6 +133,30 @@ All settings are provided through environment variables consumed by Pydantic `Ba
|
|
|
132
133
|
| `LOAD_MODULES` | Comma-separated list of agent package modules to load at startup |
|
|
133
134
|
| `NUCLIA_ZONE` | Nuclia zone (default `arag`) |
|
|
134
135
|
| `NUCLIA_PUBLIC_URL` | Public Nuclia URL template (default `https://{zone}.nuclia.com`) |
|
|
136
|
+
| `MCP_MAX_SERVERS` | Maximum cached MCP managers (default `100`) |
|
|
137
|
+
| `MCP_MAX_SESSIONS_PER_SERVER` | Maximum concurrent connections per MCP manager (default `100`) |
|
|
138
|
+
| `MCP_SESSION_IDLE_TTL_SECONDS` | Idle lifetime of each MCP connection and cached manager (default `1800`) |
|
|
139
|
+
|
|
140
|
+
### MCP connections
|
|
141
|
+
|
|
142
|
+
API and standalone applications expose their configured MCP limits through the
|
|
143
|
+
typed `mcp_settings` property, without falling back to session-server settings.
|
|
144
|
+
|
|
145
|
+
Multiple clients can use the same `/api/v1/agent/{agent_id}/session/{session}/mcp`
|
|
146
|
+
URL, including `session/ephemeral`. Each initialization receives its own
|
|
147
|
+
`Mcp-Session-Id`; subsequent requests and DELETE must include that ID. DELETE
|
|
148
|
+
only terminates the requested connection. Missing IDs on GET or DELETE return 400;
|
|
149
|
+
unknown or expired IDs return 404. Clients should initialize a new connection
|
|
150
|
+
after a 404, not reuse the expired ID.
|
|
151
|
+
|
|
152
|
+
Managers whose last connection has closed can be evicted immediately when the
|
|
153
|
+
pool is full.
|
|
154
|
+
|
|
155
|
+
MCP transports remain process-local. Multi-replica deployments must route an
|
|
156
|
+
authenticated user's initialization and subsequent requests to the same API
|
|
157
|
+
replica using a stable, trusted identity header. Hashing only `Mcp-Session-Id`
|
|
158
|
+
does not work because initialization has no session ID. Pod restarts or changes
|
|
159
|
+
to the replica set can still require reinitialization.
|
|
135
160
|
|
|
136
161
|
### Standalone A2A
|
|
137
162
|
|
|
@@ -82,6 +82,30 @@ All settings are provided through environment variables consumed by Pydantic `Ba
|
|
|
82
82
|
| `LOAD_MODULES` | Comma-separated list of agent package modules to load at startup |
|
|
83
83
|
| `NUCLIA_ZONE` | Nuclia zone (default `arag`) |
|
|
84
84
|
| `NUCLIA_PUBLIC_URL` | Public Nuclia URL template (default `https://{zone}.nuclia.com`) |
|
|
85
|
+
| `MCP_MAX_SERVERS` | Maximum cached MCP managers (default `100`) |
|
|
86
|
+
| `MCP_MAX_SESSIONS_PER_SERVER` | Maximum concurrent connections per MCP manager (default `100`) |
|
|
87
|
+
| `MCP_SESSION_IDLE_TTL_SECONDS` | Idle lifetime of each MCP connection and cached manager (default `1800`) |
|
|
88
|
+
|
|
89
|
+
### MCP connections
|
|
90
|
+
|
|
91
|
+
API and standalone applications expose their configured MCP limits through the
|
|
92
|
+
typed `mcp_settings` property, without falling back to session-server settings.
|
|
93
|
+
|
|
94
|
+
Multiple clients can use the same `/api/v1/agent/{agent_id}/session/{session}/mcp`
|
|
95
|
+
URL, including `session/ephemeral`. Each initialization receives its own
|
|
96
|
+
`Mcp-Session-Id`; subsequent requests and DELETE must include that ID. DELETE
|
|
97
|
+
only terminates the requested connection. Missing IDs on GET or DELETE return 400;
|
|
98
|
+
unknown or expired IDs return 404. Clients should initialize a new connection
|
|
99
|
+
after a 404, not reuse the expired ID.
|
|
100
|
+
|
|
101
|
+
Managers whose last connection has closed can be evicted immediately when the
|
|
102
|
+
pool is full.
|
|
103
|
+
|
|
104
|
+
MCP transports remain process-local. Multi-replica deployments must route an
|
|
105
|
+
authenticated user's initialization and subsequent requests to the same API
|
|
106
|
+
replica using a stable, trusted identity header. Hashing only `Mcp-Session-Id`
|
|
107
|
+
does not work because initialization has no session ID. Pod restarts or changes
|
|
108
|
+
to the replica set can still require reinitialization.
|
|
85
109
|
|
|
86
110
|
### Standalone A2A
|
|
87
111
|
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyperforge"
|
|
7
|
-
version = "1.0.0.
|
|
7
|
+
version = "1.0.0.post447"
|
|
8
8
|
description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
requires-python = ">=3.12"
|
|
@@ -30,6 +30,7 @@ dependencies = [
|
|
|
30
30
|
"anyio>=4.15.1",
|
|
31
31
|
"httpx>=0.28.1,<0.29",
|
|
32
32
|
"httpcore>=1.0.9,<1.1",
|
|
33
|
+
"urllib3>=2.8.0,<3",
|
|
33
34
|
"jinja2",
|
|
34
35
|
"jsonschema",
|
|
35
36
|
"RestrictedPython>=8.5",
|
|
@@ -4,7 +4,7 @@ build-backend = "uv_build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "hyperforge"
|
|
7
|
-
version = "1.0.0.
|
|
7
|
+
version = "1.0.0.post447"
|
|
8
8
|
authors = [{ name = "AI Data Team", email = "learning@nuclia.com" }]
|
|
9
9
|
description = "Agentic Framework for Orchestrated Runtime, Governance, and Execution"
|
|
10
10
|
readme = "README.md"
|
|
@@ -31,6 +31,7 @@ dependencies = [
|
|
|
31
31
|
"anyio>=4.15.1",
|
|
32
32
|
"httpx>=0.28.1,<0.29",
|
|
33
33
|
"httpcore>=1.0.9,<1.1",
|
|
34
|
+
"urllib3>=2.8.0,<3",
|
|
34
35
|
"jinja2",
|
|
35
36
|
"jsonschema",
|
|
36
37
|
"RestrictedPython>=8.5",
|
|
@@ -83,6 +83,10 @@ class HTTPApplication(FastAPI):
|
|
|
83
83
|
for extra_middleware in self.extra_middlewares:
|
|
84
84
|
self.add_middleware(extra_middleware)
|
|
85
85
|
|
|
86
|
+
@property
|
|
87
|
+
def mcp_settings(self) -> Settings:
|
|
88
|
+
return self.settings
|
|
89
|
+
|
|
86
90
|
async def startup(self) -> None:
|
|
87
91
|
GLOBAL_REGISTRY.clear()
|
|
88
92
|
await setup_telemetry(SERVICE_NAME)
|
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import asyncio
|
|
2
|
-
import json
|
|
3
2
|
import time
|
|
4
3
|
from collections.abc import Awaitable, Callable
|
|
5
4
|
from typing import Any
|
|
@@ -9,17 +8,25 @@ from fastapi import HTTPException
|
|
|
9
8
|
from mcp.server.streamable_http import MCP_SESSION_ID_HEADER
|
|
10
9
|
from mcp.server.streamable_http_manager import StreamableHTTPSessionManager
|
|
11
10
|
from starlette.requests import Request
|
|
11
|
+
from starlette.responses import Response
|
|
12
|
+
from starlette.types import Message, Receive, Scope, Send
|
|
13
|
+
|
|
14
|
+
from hyperforge.api import logger
|
|
12
15
|
|
|
13
16
|
MCPServerKey = tuple[str, str, str, str, str]
|
|
14
17
|
MCPServerFactory = Callable[[], Awaitable["ManagedMCPServer"]]
|
|
15
18
|
|
|
16
19
|
|
|
17
20
|
class ManagedMCPServer:
|
|
18
|
-
def __init__(self, manager: StreamableHTTPSessionManager):
|
|
21
|
+
def __init__(self, manager: StreamableHTTPSessionManager, max_sessions: int = 100):
|
|
19
22
|
self.manager = manager
|
|
20
23
|
self.started = asyncio.Event()
|
|
21
24
|
self.task: asyncio.Task[None] | None = None
|
|
22
|
-
self.
|
|
25
|
+
self.active_sessions = 0
|
|
26
|
+
self.max_sessions = max_sessions
|
|
27
|
+
self._session_creation_lock = anyio.Lock()
|
|
28
|
+
self._session_closed = anyio.Event()
|
|
29
|
+
self.has_been_leased = False
|
|
23
30
|
self.active_requests = 0
|
|
24
31
|
self.last_used = time.monotonic()
|
|
25
32
|
|
|
@@ -56,6 +63,7 @@ class ManagedMCPServer:
|
|
|
56
63
|
await asyncio.gather(self.task, return_exceptions=True)
|
|
57
64
|
|
|
58
65
|
def reserve_request(self) -> None:
|
|
66
|
+
self.has_been_leased = True
|
|
59
67
|
self.active_requests += 1
|
|
60
68
|
self.last_used = time.monotonic()
|
|
61
69
|
|
|
@@ -65,26 +73,42 @@ class ManagedMCPServer:
|
|
|
65
73
|
self.active_requests -= 1
|
|
66
74
|
self.last_used = time.monotonic()
|
|
67
75
|
|
|
68
|
-
def
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
76
|
+
def release_session(self) -> None:
|
|
77
|
+
self.active_sessions -= 1
|
|
78
|
+
self._session_closed.set()
|
|
79
|
+
self._session_closed = anyio.Event()
|
|
80
|
+
|
|
81
|
+
async def handle_request(self, scope: Scope, receive: Receive, send: Send) -> None:
|
|
82
|
+
request = Request(scope)
|
|
83
|
+
if request.method == "POST" and not request.headers.get(MCP_SESSION_ID_HEADER):
|
|
84
|
+
async with self._session_creation_lock:
|
|
85
|
+
if self.active_sessions >= self.max_sessions:
|
|
86
|
+
response = Response(
|
|
87
|
+
"MCP session capacity is exhausted", status_code=503
|
|
88
|
+
)
|
|
89
|
+
await response(scope, receive, send)
|
|
90
|
+
return
|
|
91
|
+
await self.manager.handle_request(scope, receive, send)
|
|
92
|
+
elif request.method == "DELETE":
|
|
93
|
+
async with self._session_creation_lock:
|
|
94
|
+
session_closed = self._session_closed
|
|
95
|
+
status: int | None = None
|
|
96
|
+
|
|
97
|
+
async def observe_send(message: Message) -> None:
|
|
98
|
+
nonlocal status
|
|
99
|
+
if message["type"] == "http.response.start":
|
|
100
|
+
status = message["status"]
|
|
101
|
+
await send(message)
|
|
102
|
+
|
|
103
|
+
await self.manager.handle_request(scope, receive, observe_send)
|
|
104
|
+
if status == 200:
|
|
105
|
+
await session_closed.wait()
|
|
106
|
+
# SDK 1.x retains explicitly terminated transports.
|
|
107
|
+
session_id = request.headers["mcp-session-id"]
|
|
108
|
+
self.manager._server_instances.pop(session_id, None)
|
|
109
|
+
self.manager._session_owners.pop(session_id, None)
|
|
110
|
+
else:
|
|
111
|
+
await self.manager.handle_request(scope, receive, send)
|
|
88
112
|
|
|
89
113
|
|
|
90
114
|
class MCPRequestLease:
|
|
@@ -109,7 +133,7 @@ class MCPServerPool:
|
|
|
109
133
|
self._idle_ttl_seconds = idle_ttl_seconds
|
|
110
134
|
self._startup_timeout_seconds = startup_timeout_seconds
|
|
111
135
|
self._servers: dict[MCPServerKey, ManagedMCPServer] = {}
|
|
112
|
-
self._creating: dict[MCPServerKey, asyncio.Task[
|
|
136
|
+
self._creating: dict[MCPServerKey, asyncio.Task[ManagedMCPServer]] = {}
|
|
113
137
|
self._retired_tasks: set[asyncio.Task[Any]] = set()
|
|
114
138
|
self._lock = anyio.Lock()
|
|
115
139
|
self._shutting_down = False
|
|
@@ -123,10 +147,6 @@ class MCPServerPool:
|
|
|
123
147
|
body: bytes,
|
|
124
148
|
factory: MCPServerFactory,
|
|
125
149
|
) -> MCPRequestLease:
|
|
126
|
-
current_task = asyncio.current_task()
|
|
127
|
-
if current_task is None:
|
|
128
|
-
raise RuntimeError("MCP request task is unavailable")
|
|
129
|
-
|
|
130
150
|
stale_server = None
|
|
131
151
|
async with self._lock:
|
|
132
152
|
self._ensure_running()
|
|
@@ -139,28 +159,38 @@ class MCPServerPool:
|
|
|
139
159
|
and time.monotonic() - server.last_used >= self._idle_ttl_seconds
|
|
140
160
|
):
|
|
141
161
|
stale_server = self._servers.pop(key)
|
|
142
|
-
|
|
162
|
+
self._retire_server(stale_server)
|
|
143
163
|
server = None
|
|
144
|
-
if server is not None and server.is_reinitialization(request, body):
|
|
145
|
-
if server.active_requests:
|
|
146
|
-
raise HTTPException(
|
|
147
|
-
status_code=409,
|
|
148
|
-
detail="MCP session is still active for this path",
|
|
149
|
-
)
|
|
150
|
-
stale_server = self._servers.pop(key)
|
|
151
|
-
stale_server.close()
|
|
152
|
-
server = None
|
|
153
|
-
|
|
154
164
|
if server is not None:
|
|
155
|
-
return self._lease(server
|
|
156
|
-
if
|
|
165
|
+
return self._lease(server)
|
|
166
|
+
if request.headers.get(MCP_SESSION_ID_HEADER):
|
|
167
|
+
raise HTTPException(status_code=404, detail="MCP session not found")
|
|
168
|
+
if request.method != "POST":
|
|
169
|
+
raise HTTPException(status_code=400, detail="Missing Mcp-Session-Id")
|
|
170
|
+
creating_task = self._creating.get(key)
|
|
171
|
+
if creating_task is None:
|
|
172
|
+
self._reserve_capacity()
|
|
173
|
+
creating_task = asyncio.create_task(
|
|
174
|
+
self._create_server(key, factory, stale_server)
|
|
175
|
+
)
|
|
176
|
+
self._creating[key] = creating_task
|
|
177
|
+
creating_task.add_done_callback(self._log_creation_failure)
|
|
178
|
+
|
|
179
|
+
created = await asyncio.shield(creating_task)
|
|
180
|
+
async with self._lock:
|
|
181
|
+
self._ensure_running()
|
|
182
|
+
if self._servers.get(key) is not created:
|
|
157
183
|
raise HTTPException(
|
|
158
|
-
status_code=
|
|
159
|
-
detail="MCP session initialization is already in progress",
|
|
184
|
+
status_code=503, detail="MCP server initialization was interrupted"
|
|
160
185
|
)
|
|
161
|
-
self.
|
|
162
|
-
self._creating[key] = current_task
|
|
186
|
+
return self._lease(created)
|
|
163
187
|
|
|
188
|
+
async def _create_server(
|
|
189
|
+
self,
|
|
190
|
+
key: MCPServerKey,
|
|
191
|
+
factory: MCPServerFactory,
|
|
192
|
+
stale_server: ManagedMCPServer | None,
|
|
193
|
+
) -> ManagedMCPServer:
|
|
164
194
|
candidate = None
|
|
165
195
|
registered = False
|
|
166
196
|
try:
|
|
@@ -172,19 +202,26 @@ class MCPServerPool:
|
|
|
172
202
|
async with self._lock:
|
|
173
203
|
self._ensure_running()
|
|
174
204
|
self._evict_for_capacity()
|
|
175
|
-
lease = self._lease(candidate, request)
|
|
176
205
|
self._servers[key] = candidate
|
|
177
206
|
self._creating.pop(key, None)
|
|
178
207
|
registered = True
|
|
179
|
-
return
|
|
208
|
+
return candidate
|
|
180
209
|
except BaseException:
|
|
181
210
|
if candidate is not None and not registered:
|
|
182
211
|
await candidate.stop()
|
|
183
212
|
async with self._lock:
|
|
184
|
-
if self._creating.get(key) is current_task:
|
|
213
|
+
if self._creating.get(key) is asyncio.current_task():
|
|
185
214
|
self._creating.pop(key, None)
|
|
186
215
|
raise
|
|
187
216
|
|
|
217
|
+
@staticmethod
|
|
218
|
+
def _log_creation_failure(task: asyncio.Task[ManagedMCPServer]) -> None:
|
|
219
|
+
if not task.cancelled() and (error := task.exception()) is not None:
|
|
220
|
+
logger.error(
|
|
221
|
+
"MCP server initialization failed",
|
|
222
|
+
exc_info=(type(error), error, error.__traceback__),
|
|
223
|
+
)
|
|
224
|
+
|
|
188
225
|
async def remove(self, key: MCPServerKey) -> None:
|
|
189
226
|
async with self._lock:
|
|
190
227
|
server = self._servers.pop(key, None)
|
|
@@ -257,12 +294,7 @@ class MCPServerPool:
|
|
|
257
294
|
if self._shutting_down:
|
|
258
295
|
raise HTTPException(status_code=503, detail="MCP service is shutting down")
|
|
259
296
|
|
|
260
|
-
def _lease(self, server: ManagedMCPServer
|
|
261
|
-
if not server.accept_request(request):
|
|
262
|
-
raise HTTPException(
|
|
263
|
-
status_code=409,
|
|
264
|
-
detail="MCP session already initialized for this path",
|
|
265
|
-
)
|
|
297
|
+
def _lease(self, server: ManagedMCPServer) -> MCPRequestLease:
|
|
266
298
|
server.reserve_request()
|
|
267
299
|
return MCPRequestLease(server)
|
|
268
300
|
|
|
@@ -281,7 +313,7 @@ class MCPServerPool:
|
|
|
281
313
|
for key, server in self._servers.items()
|
|
282
314
|
if server.active_requests == 0
|
|
283
315
|
and (
|
|
284
|
-
|
|
316
|
+
(server.has_been_leased and server.active_sessions == 0)
|
|
285
317
|
or now - server.last_used >= self._idle_ttl_seconds
|
|
286
318
|
)
|
|
287
319
|
]
|
|
@@ -292,6 +324,10 @@ class MCPServerPool:
|
|
|
292
324
|
)
|
|
293
325
|
key, server = min(candidates, key=lambda item: item[1].last_used)
|
|
294
326
|
self._servers.pop(key)
|
|
327
|
+
|
|
328
|
+
self._retire_server(server)
|
|
329
|
+
|
|
330
|
+
def _retire_server(self, server: ManagedMCPServer) -> None:
|
|
295
331
|
server.close()
|
|
296
332
|
if server.task is not None:
|
|
297
333
|
self._retired_tasks.add(server.task)
|
|
@@ -43,5 +43,6 @@ class Settings(BaseSettings):
|
|
|
43
43
|
mcp_max_request_bytes: int = Field(default=1024 * 1024, ge=1)
|
|
44
44
|
mcp_max_response_bytes: int = Field(default=4 * 1024 * 1024, ge=1)
|
|
45
45
|
mcp_max_servers: int = Field(default=100, ge=1)
|
|
46
|
+
mcp_max_sessions_per_server: int = Field(default=100, ge=1)
|
|
46
47
|
mcp_session_idle_ttl_seconds: int = Field(default=1800, ge=1)
|
|
47
48
|
mcp_startup_timeout_seconds: int = Field(default=30, ge=1)
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/api/v1/mcp_interaction.py
RENAMED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import asyncio
|
|
2
2
|
import json
|
|
3
3
|
import uuid
|
|
4
|
+
from contextlib import asynccontextmanager
|
|
4
5
|
from functools import partial
|
|
5
|
-
from typing import TYPE_CHECKING, Any, Iterable, Sequence
|
|
6
|
+
from typing import TYPE_CHECKING, Any, AsyncIterator, Iterable, Sequence
|
|
6
7
|
|
|
7
8
|
from fastapi import Header, HTTPException
|
|
8
9
|
from mcp.server.fastmcp.exceptions import ResourceError
|
|
@@ -283,12 +284,10 @@ def _get_mcp_auth_config(app: "HTTPApplication", agent_id: str):
|
|
|
283
284
|
|
|
284
285
|
|
|
285
286
|
def _get_first_enabled_mcp_auth_config(app: "HTTPApplication"):
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
auth_config
|
|
290
|
-
if auth_config is not None:
|
|
291
|
-
return auth_config
|
|
287
|
+
for agent_id in app._agents_cfg:
|
|
288
|
+
auth_config = get_enabled_mcp_auth(app._agents_cfg, agent_id)
|
|
289
|
+
if auth_config is not None:
|
|
290
|
+
return auth_config
|
|
292
291
|
return None
|
|
293
292
|
|
|
294
293
|
|
|
@@ -300,6 +299,17 @@ async def _create_mcp_server(
|
|
|
300
299
|
account: str,
|
|
301
300
|
max_request_bytes: int,
|
|
302
301
|
) -> ManagedMCPServer:
|
|
302
|
+
runtime_settings = app.mcp_settings
|
|
303
|
+
|
|
304
|
+
@asynccontextmanager
|
|
305
|
+
async def mcp_lifespan(server: MCPServer) -> AsyncIterator[dict[str, Any]]:
|
|
306
|
+
managed_server.active_sessions += 1
|
|
307
|
+
try:
|
|
308
|
+
async with default_lifespan(server) as context:
|
|
309
|
+
yield context
|
|
310
|
+
finally:
|
|
311
|
+
managed_server.release_session()
|
|
312
|
+
|
|
303
313
|
agent_manager: AgentManager = request.app.agent_manager
|
|
304
314
|
workflows, agent_config, prompts = await asyncio.gather(
|
|
305
315
|
agent_manager.workflows_list(account=account, agent_id=agent_id),
|
|
@@ -310,7 +320,7 @@ async def _create_mcp_server(
|
|
|
310
320
|
name=agent_id,
|
|
311
321
|
version="1.0.0",
|
|
312
322
|
instructions=agent_config.instructions,
|
|
313
|
-
lifespan=
|
|
323
|
+
lifespan=mcp_lifespan,
|
|
314
324
|
)
|
|
315
325
|
mcp_server.list_tools()(partial(list_tools, workflows))
|
|
316
326
|
mcp_server.call_tool()(
|
|
@@ -331,9 +341,12 @@ async def _create_mcp_server(
|
|
|
331
341
|
json_response=True,
|
|
332
342
|
stateless=False,
|
|
333
343
|
security_settings=None,
|
|
344
|
+
session_idle_timeout=runtime_settings.mcp_session_idle_ttl_seconds,
|
|
334
345
|
max_request_body_size=max_request_bytes,
|
|
335
346
|
)
|
|
336
|
-
managed_server = ManagedMCPServer(
|
|
347
|
+
managed_server = ManagedMCPServer(
|
|
348
|
+
manager, max_sessions=runtime_settings.mcp_max_sessions_per_server
|
|
349
|
+
)
|
|
337
350
|
await managed_server.start()
|
|
338
351
|
return managed_server
|
|
339
352
|
|
|
@@ -374,9 +387,7 @@ class _MCPTransportResponse(Response):
|
|
|
374
387
|
return await receive()
|
|
375
388
|
|
|
376
389
|
if self.request.method == "GET":
|
|
377
|
-
await self.managed_server.
|
|
378
|
-
scope, patched_receive, send
|
|
379
|
-
)
|
|
390
|
+
await self.managed_server.handle_request(scope, patched_receive, send)
|
|
380
391
|
return
|
|
381
392
|
|
|
382
393
|
response_status = 200
|
|
@@ -404,7 +415,7 @@ class _MCPTransportResponse(Response):
|
|
|
404
415
|
elif not response_too_large:
|
|
405
416
|
body_chunks.append(chunk)
|
|
406
417
|
|
|
407
|
-
await self.managed_server.
|
|
418
|
+
await self.managed_server.handle_request(
|
|
408
419
|
scope, patched_receive, intercepting_send
|
|
409
420
|
)
|
|
410
421
|
if response_too_large:
|
|
@@ -498,20 +509,6 @@ async def mcp_interaction_protected_resource_metadata(
|
|
|
498
509
|
|
|
499
510
|
|
|
500
511
|
@router.delete("/api/v1/agent/{agent_id}/session/{session}/mcp", tags=["MCP"])
|
|
501
|
-
@requires_one([AgentRole.MEMBER])
|
|
502
|
-
async def mcp_handler_delete(
|
|
503
|
-
request: Request,
|
|
504
|
-
agent_id: str,
|
|
505
|
-
session: str,
|
|
506
|
-
x_stf_user: str = Header(..., include_in_schema=False),
|
|
507
|
-
x_stf_account: str = Header(..., include_in_schema=False),
|
|
508
|
-
x_stf_account_type: str = Header(..., include_in_schema=False),
|
|
509
|
-
):
|
|
510
|
-
app: HTTPApplication = request.app
|
|
511
|
-
key = (x_stf_account, x_stf_user, x_stf_account_type, agent_id, session)
|
|
512
|
-
await app.mcp_server_pool.remove(key)
|
|
513
|
-
|
|
514
|
-
|
|
515
512
|
@router.get("/api/v1/agent/{agent_id}/session/{session}/mcp", tags=["MCP"])
|
|
516
513
|
@router.post("/api/v1/agent/{agent_id}/session/{session}/mcp", tags=["MCP"])
|
|
517
514
|
@requires_one([AgentRole.MEMBER])
|
|
@@ -524,7 +521,11 @@ async def interaction_mcp_handler(
|
|
|
524
521
|
x_stf_account_type: str = Header(..., include_in_schema=False),
|
|
525
522
|
):
|
|
526
523
|
app: HTTPApplication = request.app
|
|
527
|
-
|
|
524
|
+
if request.method in {"GET", "DELETE"} and not request.headers.get(
|
|
525
|
+
"mcp-session-id"
|
|
526
|
+
):
|
|
527
|
+
raise HTTPException(status_code=400, detail="Missing Mcp-Session-Id")
|
|
528
|
+
runtime_settings = app.mcp_settings
|
|
528
529
|
max_request_bytes = runtime_settings.mcp_max_request_bytes
|
|
529
530
|
max_response_bytes = runtime_settings.mcp_max_response_bytes
|
|
530
531
|
content_length = request.headers.get("content-length")
|
|
@@ -321,6 +321,10 @@ class StandaloneApplication(FastAPI):
|
|
|
321
321
|
def settings(self) -> ServerSettings:
|
|
322
322
|
return self._server_settings
|
|
323
323
|
|
|
324
|
+
@property
|
|
325
|
+
def mcp_settings(self) -> StandaloneSettings:
|
|
326
|
+
return self._standalone_settings
|
|
327
|
+
|
|
324
328
|
# ------------------------------------------------------------------
|
|
325
329
|
# Lifecycle
|
|
326
330
|
# ------------------------------------------------------------------
|
|
@@ -192,6 +192,7 @@ class StandaloneSettings(BaseSettings):
|
|
|
192
192
|
mcp_max_request_bytes: int = Field(default=1024 * 1024, ge=1)
|
|
193
193
|
mcp_max_response_bytes: int = Field(default=4 * 1024 * 1024, ge=1)
|
|
194
194
|
mcp_max_servers: int = Field(default=100, ge=1)
|
|
195
|
+
mcp_max_sessions_per_server: int = Field(default=100, ge=1)
|
|
195
196
|
mcp_session_idle_ttl_seconds: int = Field(default=1800, ge=1)
|
|
196
197
|
mcp_startup_timeout_seconds: int = Field(default=30, ge=1)
|
|
197
198
|
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/legacy_llm_migration.py
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/db/llm_migration_utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/__init__.py
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/codemode.py
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/core.py
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/harness_sdk/tools/utils.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/procedural/evolution.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/standalone/ui_router.py
RENAMED
|
File without changes
|
{hyperforge-1.0.0.post437 → hyperforge-1.0.0.post447}/src/hyperforge/static/assets/index-DLejS5jC.js
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|