mainsequence 9.0.22.dev65__tar.gz → 9.0.23.dev66__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.
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/PKG-INFO +2 -2
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/AGENTS.md +2 -1
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/application_surfaces/api_surfaces/SKILL.md +59 -5
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/maintenance/code_repository_maintenance/SKILL.md +10 -6
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/sdk_code_repository_execution/SKILL.md +2 -1
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/cli.py +24 -27
- mainsequence-9.0.23.dev66/mainsequence/server/_mcp_skills_upstream/__init__.py +60 -0
- mainsequence-9.0.23.dev66/mainsequence/server/_mcp_skills_upstream/server.py +172 -0
- mainsequence-9.0.23.dev66/mainsequence/server/_mcp_skills_upstream/shared.py +297 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/server/fastapi.py +3 -1
- mainsequence-9.0.23.dev66/mainsequence/server/mcp_skills.py +408 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/PKG-INFO +2 -2
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/SOURCES.txt +5 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/requires.txt +1 -1
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/pyproject.toml +18 -5
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_fastapi_mcp.py +9 -9
- mainsequence-9.0.23.dev66/tests/test_mcp_skills.py +573 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/LICENSE +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/README.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/a2a_sdk_execution/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/mainsequence-inference/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/maintenance/bug_auditor/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/maintenance/metatables_transition/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/platform_operations/access_control_and_sharing/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/agent_scaffold/skills/platform_operations/orchestration_and_releases/SKILL.md +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/__init__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/__main__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/_request_identity.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/_transport.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/bootstrap.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/__init__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/api.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/browser_auth.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/code_repository_status.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/config.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/docker_utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/doctor.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/local_ops.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/model_filters.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/pydantic_cli.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/sdk_utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/ssh_utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/cli/ui.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/__init__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/agent_runtime_models.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/base.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/compute_validation.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/exceptions.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/github_issues.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/inference.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/models_foundry.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/models_helpers.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/models_user.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/observability.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/client/value_sets.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/code_repository_context.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/code_repository_skills.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/defaults.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/instrumentation/__init__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/instrumentation/utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/logconf.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/repository_identity_security.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/runtime_credential_exchange.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/runtime_flags.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/scaffold_skills.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/server/__init__.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence/server/caller_assertions.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/dependency_links.txt +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/entry_points.txt +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/mainsequence.egg-info/top_level.txt +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/setup.cfg +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_auth_bootstrap.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_auth_precedence.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_caller_assertions.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_canonical_api_route_contract.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_cli_auth_contract.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_cli_browser_auth.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_cli_request_budget.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_cli_secure_storage.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_code_repository_branch_contract.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_code_repository_documentation_contract.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_code_repository_runtime_context.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_code_repository_skills.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_credential_repr.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_dependency_extras.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_dev_release_version.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_environment_resource_context.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_environment_response_contracts.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_fastapi_request_identity.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_github_issues_client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_http_retry_budget.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_independent_context.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_inference_client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_instrumentation.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_job_client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_local_ops.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_logconf.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_models_user_request_bound_auth.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_notification_uid_contract.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_observability_client.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_runtime_credential_exchange.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_scaffold_skills.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_secret_client_model.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_source_ownership.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_ssh_utils.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_tolerant_response_reading.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_tolerant_value_sets.py +0 -0
- {mainsequence-9.0.22.dev65 → mainsequence-9.0.23.dev66}/tests/test_user_application_facts.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: mainsequence
|
|
3
|
-
Version: 9.0.
|
|
3
|
+
Version: 9.0.23.dev66
|
|
4
4
|
Summary: Main Sequence SDK
|
|
5
5
|
Author-email: Main Sequence GmbH <dev@main-sequence.io>
|
|
6
6
|
License-Expression: MIT
|
|
@@ -32,7 +32,7 @@ Requires-Dist: structlog
|
|
|
32
32
|
Requires-Dist: typer
|
|
33
33
|
Requires-Dist: urllib3>=2.7.0
|
|
34
34
|
Provides-Extra: mcp
|
|
35
|
-
Requires-Dist: mcp<2
|
|
35
|
+
Requires-Dist: mcp<3,>=2.3; extra == "mcp"
|
|
36
36
|
Dynamic: license-file
|
|
37
37
|
|
|
38
38
|
<p align="center">
|
|
@@ -77,7 +77,8 @@ bundle. Platform skills are installed separately under
|
|
|
77
77
|
`mainsequence.client.metatables`, or calls the retired `mainsequence` table
|
|
78
78
|
and migration commands:
|
|
79
79
|
`.agents/skills/mainsequence/maintenance/metatables_transition/SKILL.md`
|
|
80
|
-
- FastAPI APIs serving the Command Center frontend
|
|
80
|
+
- FastAPI APIs serving the Command Center frontend, and MCP tools and skills
|
|
81
|
+
served from a FastAPI release:
|
|
81
82
|
`.agents/skills/mainsequence/application_surfaces/api_surfaces/SKILL.md`
|
|
82
83
|
- Jobs, schedules, images, resources, releases, and Artifacts:
|
|
83
84
|
`.agents/skills/mainsequence/platform_operations/orchestration_and_releases/SKILL.md`
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: mainsequence-command-center-fastapi
|
|
3
|
-
description: Build, contract-test, release, and verify a FastAPI code repository resource serving the Command Center frontend, including applications with public OAuth callback or webhook routes. Use when implementing a Command Center wire contract, declaring exact public ingress, or validating the deployed API.
|
|
3
|
+
description: Build, contract-test, release, and verify a FastAPI code repository resource serving the Command Center frontend, including applications with public OAuth callback or webhook routes. Use when implementing a Command Center wire contract, declaring exact public ingress, serving MCP tools or publishing MCP skills from the app (including setting the folder of skills it publishes), or validating the deployed API.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Command Center FastAPI Release Lifecycle
|
|
@@ -236,10 +236,11 @@ See also
|
|
|
236
236
|
|
|
237
237
|
### MCP tools
|
|
238
238
|
|
|
239
|
-
To serve MCP tools from the same application, add the `mcp` extra
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
239
|
+
To serve MCP tools from the same application, add the `mcp` extra (the
|
|
240
|
+
official MCP Python SDK 2.x) and call
|
|
241
|
+
`install_mcp(app, mcp.streamable_http_app(stateless_http=True, json_response=True, transport_security=...), lifespan=mcp.session_manager.run)`
|
|
242
|
+
after `install_request_identity(app)`, with an `MCPServer`; 1.x's `FastMCP`
|
|
243
|
+
is not supported by the extra. Tools authorize with `User.get_logged_user()` and
|
|
243
244
|
`User.get_requester()` exactly like REST handlers; admission to the release
|
|
244
245
|
does not authorize every tool. Each tool decides whether it needs a person: a
|
|
245
246
|
tool that needs one fails with its own error when the getter returns `None`,
|
|
@@ -251,6 +252,59 @@ threads it uses through `asyncio.to_thread` or anyio's `to_thread.run_sync`,
|
|
|
251
252
|
and ends with the call. See
|
|
252
253
|
[Serving MCP](https://mainsequence-sdk.github.io/mainsequence-sdk/knowledge/fastapi/#serving-mcp).
|
|
253
254
|
|
|
255
|
+
### MCP skills
|
|
256
|
+
|
|
257
|
+
The MCP server publishes skills to its clients only from the folder set with
|
|
258
|
+
`skills_extension`. Setting it is required: without it, the server exposes no
|
|
259
|
+
skills and declares no Skills extension, whatever the repository holds.
|
|
260
|
+
|
|
261
|
+
1. Ask the user which folder holds the skills to publish. Its name and place
|
|
262
|
+
are the user's choice; the SDK neither requires nor suggests one. The
|
|
263
|
+
examples below use `.agents/skills/mcp_only`. Only that folder is published;
|
|
264
|
+
the coding agents' other skill folders are not.
|
|
265
|
+
2. In that folder, a folder with a `SKILL.md` is a skill, and a folder without
|
|
266
|
+
one groups skills; the group's path prefixes the URIs of the skills in it.
|
|
267
|
+
Each `SKILL.md` has frontmatter with `name`, equal to its folder's name, and
|
|
268
|
+
`description`. Supporting files sit beside it, in any subfolders:
|
|
269
|
+
|
|
270
|
+
```text
|
|
271
|
+
.agents/skills/mcp_only/ # the folder set below (an example)
|
|
272
|
+
billing/ # a group: no SKILL.md
|
|
273
|
+
refund-review/
|
|
274
|
+
SKILL.md # skill://billing/refund-review/SKILL.md
|
|
275
|
+
references/policy.md
|
|
276
|
+
chargebacks/
|
|
277
|
+
SKILL.md # skill://billing/chargebacks/SKILL.md
|
|
278
|
+
daily-close/
|
|
279
|
+
SKILL.md # skill://daily-close/SKILL.md
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
Skill and group folder names are 1 to 64 characters of `a-z`, `0-9` and
|
|
283
|
+
single hyphens. A skill inside another skill is refused.
|
|
284
|
+
3. Set the folder when constructing the server. A relative path is
|
|
285
|
+
repository-relative: it is looked up from the module that calls
|
|
286
|
+
`skills_extension` upwards, so it works from any module of the repository
|
|
287
|
+
and any working directory. Point at the folder that holds the skills, never
|
|
288
|
+
at `.agents/skills` itself:
|
|
289
|
+
|
|
290
|
+
```python
|
|
291
|
+
from mcp.server.mcpserver import MCPServer
|
|
292
|
+
|
|
293
|
+
from mainsequence.server.mcp_skills import skills_extension
|
|
294
|
+
|
|
295
|
+
mcp = MCPServer("refunds", extensions=[skills_extension(".agents/skills/mcp_only")])
|
|
296
|
+
```
|
|
297
|
+
|
|
298
|
+
4. Commit the folder and start the app. Every skill in it is published; add or
|
|
299
|
+
remove a skill folder and redeploy to change the list. An invalid skill
|
|
300
|
+
fails startup with the file and the rule; quote frontmatter values such as
|
|
301
|
+
`yes`, dates and `0755`.
|
|
302
|
+
|
|
303
|
+
Clients list the skills with `skills/list` and read them with `skills/get`
|
|
304
|
+
and `resources/read` on the release's `/mcp`, behind the same authentication
|
|
305
|
+
as tools. Do not register your own `skill://` resources. See
|
|
306
|
+
[Skills](https://mainsequence-sdk.github.io/mainsequence-sdk/knowledge/fastapi/#skills).
|
|
307
|
+
|
|
254
308
|
### Public provider callbacks and webhooks
|
|
255
309
|
|
|
256
310
|
An external OAuth redirect or webhook cannot supply a Main Sequence Bearer token.
|
|
@@ -162,18 +162,22 @@ mainsequence code-repository update-sdk --path . --dry-run
|
|
|
162
162
|
mainsequence code-repository update-sdk --path .
|
|
163
163
|
```
|
|
164
164
|
|
|
165
|
-
`update-sdk` updates the lock and local environment
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
165
|
+
`update-sdk` updates the lock and local environment, then runs the newly
|
|
166
|
+
installed SDK's skill copier in a fresh process against the same repository.
|
|
167
|
+
It replaces SDK-owned skills, removes retired ones, and updates their sentinel.
|
|
168
|
+
It does not refresh platform skills or `AGENTS.md`, or publish the working tree.
|
|
169
|
+
If copying fails, the command fails but the SDK remains upgraded; report that
|
|
170
|
+
partial result and use the printed refresh command to retry after addressing
|
|
171
|
+
the failure. On success, reload this maintenance skill before continuing.
|
|
169
172
|
|
|
170
173
|
Do not automatically commit or push an SDK update unless the user also asked
|
|
171
174
|
to publish the CodeRepository changes.
|
|
172
175
|
|
|
173
176
|
## Refresh Managed Skills And Instructions
|
|
174
177
|
|
|
175
|
-
|
|
176
|
-
|
|
178
|
+
Outside the skill refresh included in `update-sdk`, run either managed-scaffold
|
|
179
|
+
command only when the user explicitly requests that specific update. When both
|
|
180
|
+
are requested, refresh the SDK-owned skills first,
|
|
177
181
|
then update the managed Main Sequence block in
|
|
178
182
|
`AGENTS.md`:
|
|
179
183
|
|
|
@@ -49,7 +49,8 @@ Do not let this skill become a domain manual.
|
|
|
49
49
|
|
|
50
50
|
- MetaTables, table updates, and domain data discovery: use the installed
|
|
51
51
|
`metatables` package skills and documentation
|
|
52
|
-
- FastAPI APIs serving the Command Center frontend
|
|
52
|
+
- FastAPI APIs serving the Command Center frontend, and MCP tools and skills
|
|
53
|
+
served from a FastAPI release:
|
|
53
54
|
`.agents/skills/mainsequence/application_surfaces/api_surfaces/SKILL.md`
|
|
54
55
|
- CodeRepository audits, blocker analysis, and upstream SDK assessment:
|
|
55
56
|
`.agents/skills/mainsequence/maintenance/bug_auditor/SKILL.md`
|
|
@@ -7633,7 +7633,10 @@ def code_repository_update_sdk(
|
|
|
7633
7633
|
dry_run: bool = typer.Option(False, "--dry-run", help="Print steps but do not execute"),
|
|
7634
7634
|
):
|
|
7635
7635
|
"""
|
|
7636
|
-
Upgrade the CodeRepository SDK dependency
|
|
7636
|
+
Upgrade the CodeRepository SDK dependency and refresh its SDK-owned skills.
|
|
7637
|
+
|
|
7638
|
+
After installing with `uv`, run the updated SDK's skill copier in a fresh
|
|
7639
|
+
process. Platform skills and AGENTS.md are not changed.
|
|
7637
7640
|
|
|
7638
7641
|
Parameters
|
|
7639
7642
|
----------
|
|
@@ -7655,10 +7658,20 @@ def code_repository_update_sdk(
|
|
|
7655
7658
|
code_repository_dir = _resolve_code_repository_dir(code_repository_id, path)
|
|
7656
7659
|
ensure_venv(code_repository_dir)
|
|
7657
7660
|
|
|
7661
|
+
refresh_args = [
|
|
7662
|
+
"run",
|
|
7663
|
+
"--no-sync",
|
|
7664
|
+
"mainsequence",
|
|
7665
|
+
"code-repository",
|
|
7666
|
+
"update-agent-skills",
|
|
7667
|
+
"--path",
|
|
7668
|
+
str(code_repository_dir),
|
|
7669
|
+
]
|
|
7658
7670
|
steps = [
|
|
7659
7671
|
"resolve uv executable",
|
|
7660
7672
|
"uv lock --upgrade-package mainsequence",
|
|
7661
7673
|
"uv sync",
|
|
7674
|
+
f"uv {shlex.join(refresh_args)}",
|
|
7662
7675
|
]
|
|
7663
7676
|
print_table("Update SDK plan", ["Step"], [[s] for s in steps])
|
|
7664
7677
|
|
|
@@ -7671,34 +7684,18 @@ def code_repository_update_sdk(
|
|
|
7671
7684
|
run_uv(uv, ["lock", "--upgrade-package", "mainsequence"], cwd=code_repository_dir)
|
|
7672
7685
|
run_uv(uv, ["sync"], cwd=code_repository_dir)
|
|
7673
7686
|
|
|
7674
|
-
|
|
7675
|
-
_warn_if_sdk_skills_stale(code_repository_dir)
|
|
7676
|
-
|
|
7677
|
-
|
|
7678
|
-
def _warn_if_sdk_skills_stale(code_repository_dir: pathlib.Path) -> None:
|
|
7687
|
+
# The current process still has the pre-upgrade CLI loaded.
|
|
7679
7688
|
try:
|
|
7680
|
-
|
|
7681
|
-
|
|
7682
|
-
|
|
7683
|
-
|
|
7684
|
-
|
|
7685
|
-
|
|
7686
|
-
if "=" in line
|
|
7687
|
-
)
|
|
7688
|
-
if sentinel.is_file()
|
|
7689
|
-
else {}
|
|
7690
|
-
)
|
|
7691
|
-
except (OSError, ValueError, typer.Exit):
|
|
7692
|
-
warn("Could not check the vendored SDK skill pin. Run update-agent-skills to refresh it.")
|
|
7693
|
-
return
|
|
7694
|
-
pinned = values.get("pinned_version", values.get("sdk_version"))
|
|
7695
|
-
if pinned != version:
|
|
7696
|
-
warn(
|
|
7697
|
-
f"Vendored SDK skills are stale or missing (pinned: {pinned or 'missing'}; installed: {version}). "
|
|
7698
|
-
"No scaffold files were changed. Refresh with "
|
|
7699
|
-
"`uv run --offline mainsequence code-repository update-agent-skills --path .` "
|
|
7700
|
-
"and `uv run --offline mainsequence code-repository update AGENTS.md --path .`."
|
|
7689
|
+
run_uv(uv, refresh_args, cwd=code_repository_dir)
|
|
7690
|
+
except (OSError, RuntimeError) as exc:
|
|
7691
|
+
error(
|
|
7692
|
+
f"SDK upgraded, but SDK skill refresh failed: {exc}\n"
|
|
7693
|
+
"The SDK upgrade has not been rolled back. "
|
|
7694
|
+
f"Retry from {code_repository_dir} with: uv {shlex.join(refresh_args)}"
|
|
7701
7695
|
)
|
|
7696
|
+
raise typer.Exit(1) from exc
|
|
7697
|
+
|
|
7698
|
+
success("SDK update and SDK skill refresh complete.")
|
|
7702
7699
|
|
|
7703
7700
|
|
|
7704
7701
|
@code_repository.command("update")
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
"""The official MCP Skills extension, copied until an ``mcp`` release ships it (ADR 0039).
|
|
2
|
+
|
|
3
|
+
``server.py`` and ``shared.py`` are ``src/mcp/server/skills.py`` and
|
|
4
|
+
``src/mcp/shared/skills.py`` of the official MCP Python SDK,
|
|
5
|
+
https://github.com/modelcontextprotocol/python-sdk, at commit
|
|
6
|
+
``9a754b0aabb873d9707d52dc165b569fb442ec8c`` ("Add `Skills` extension", #3485),
|
|
7
|
+
the server side of the ``io.modelcontextprotocol/skills`` extension. They run on
|
|
8
|
+
the released ``mcp`` 2.3.0.
|
|
9
|
+
|
|
10
|
+
The only change is one import in ``server.py``, which reads ``shared.py`` from
|
|
11
|
+
this package instead of ``mcp.shared.skills``. Nothing else is edited, fixed or
|
|
12
|
+
extended; take an upstream fix by copying both files again from a later commit.
|
|
13
|
+
``UPSTREAM`` records each file's SHA-256 as upstream published it, and a test
|
|
14
|
+
checks that the copies still match. Delete this package once an ``mcp`` release
|
|
15
|
+
contains the extension.
|
|
16
|
+
|
|
17
|
+
The copied files are under the license of the MCP Python SDK:
|
|
18
|
+
|
|
19
|
+
MIT License
|
|
20
|
+
|
|
21
|
+
Copyright (c) 2024 Anthropic, PBC
|
|
22
|
+
|
|
23
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
24
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
25
|
+
in the Software without restriction, including without limitation the rights
|
|
26
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
27
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
28
|
+
furnished to do so, subject to the following conditions:
|
|
29
|
+
|
|
30
|
+
The above copyright notice and this permission notice shall be included in all
|
|
31
|
+
copies or substantial portions of the Software.
|
|
32
|
+
|
|
33
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
34
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
35
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
36
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
37
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
38
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
39
|
+
SOFTWARE.
|
|
40
|
+
"""
|
|
41
|
+
|
|
42
|
+
COMMIT = "9a754b0aabb873d9707d52dc165b569fb442ec8c"
|
|
43
|
+
|
|
44
|
+
# Copied file: (upstream path, SHA-256 of the upstream file).
|
|
45
|
+
UPSTREAM = {
|
|
46
|
+
"server.py": (
|
|
47
|
+
"src/mcp/server/skills.py",
|
|
48
|
+
"4a7e9b1db05d693b07c45a4db4ef85ef54713708a96154841255aa3c8d8a5334",
|
|
49
|
+
),
|
|
50
|
+
"shared.py": (
|
|
51
|
+
"src/mcp/shared/skills.py",
|
|
52
|
+
"2669d7f4fa0ba328bdde70589d410f58274f5e8aff9d01e31e9b500d7a45ccfb",
|
|
53
|
+
),
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
# The one line changed in server.py: (upstream, copy).
|
|
57
|
+
CHANGED_IMPORT = (
|
|
58
|
+
"from mcp.shared.skills import (",
|
|
59
|
+
"from mainsequence.server._mcp_skills_upstream.shared import (",
|
|
60
|
+
)
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""The Skills extension (`io.modelcontextprotocol/skills`, SEP-2640).
|
|
2
|
+
|
|
3
|
+
SEP-2640 defines a convention for serving Agent Skills over MCP using the
|
|
4
|
+
Resources primitive: a skill is a directory of files, conventionally exposed
|
|
5
|
+
under the `skill://` scheme, and enumerated and fetched through two required
|
|
6
|
+
methods (`skills/list`, `skills/get`) plus one optional one
|
|
7
|
+
(`resources/directory/read`). See
|
|
8
|
+
https://github.com/modelcontextprotocol/modelcontextprotocol/pull/2640.
|
|
9
|
+
|
|
10
|
+
This module provides the protocol-level plumbing only: request/response
|
|
11
|
+
handling, SEP-2640 conformance validation, and capability advertisement. It
|
|
12
|
+
does not discover, read, or hash skills from a filesystem — a server author
|
|
13
|
+
supplies handlers that answer `skills/list`/`skills/get`/`resources/directory/read`
|
|
14
|
+
however their catalog is stored, and serves the underlying `skill://` file
|
|
15
|
+
content through the server's ordinary resource-registration APIs
|
|
16
|
+
(`MCPServer.add_resource`, or an `@mcp.resource(...)` template).
|
|
17
|
+
|
|
18
|
+
async def list_skills(ctx, params):
|
|
19
|
+
return ListSkillsResult(skills=[...])
|
|
20
|
+
|
|
21
|
+
async def get_skill(ctx, params):
|
|
22
|
+
if params.uri != "skill://git-workflow/SKILL.md":
|
|
23
|
+
raise MCPError(code=INVALID_PARAMS, message="unknown skill")
|
|
24
|
+
return GetSkillResult(skill=...)
|
|
25
|
+
|
|
26
|
+
mcp = MCPServer("catalog", extensions=[Skills(list_skills=list_skills, get_skill=get_skill)])
|
|
27
|
+
"""
|
|
28
|
+
|
|
29
|
+
from __future__ import annotations
|
|
30
|
+
|
|
31
|
+
from collections.abc import Awaitable, Callable, Sequence
|
|
32
|
+
from typing import Any
|
|
33
|
+
|
|
34
|
+
from mcp_types import CacheableResult
|
|
35
|
+
from mcp_types.jsonrpc import INTERNAL_ERROR, INVALID_PARAMS
|
|
36
|
+
from mcp_types.version import MODERN_PROTOCOL_VERSIONS
|
|
37
|
+
from pydantic import ValidationError
|
|
38
|
+
|
|
39
|
+
from mcp.server.context import HandlerResult, ServerRequestContext
|
|
40
|
+
from mcp.server.extension import Extension, MethodBinding
|
|
41
|
+
from mcp.shared.exceptions import MCPError
|
|
42
|
+
from mainsequence.server._mcp_skills_upstream.shared import (
|
|
43
|
+
EXTENSION_ID,
|
|
44
|
+
METHOD_GET,
|
|
45
|
+
METHOD_LIST,
|
|
46
|
+
METHOD_READ_DIRECTORY,
|
|
47
|
+
GetSkillParams,
|
|
48
|
+
GetSkillResult,
|
|
49
|
+
ListSkillsParams,
|
|
50
|
+
ListSkillsResult,
|
|
51
|
+
ReadDirectoryParams,
|
|
52
|
+
ReadDirectoryResult,
|
|
53
|
+
parse_directory_uri,
|
|
54
|
+
skill_name_from_uri,
|
|
55
|
+
validate_directory_result,
|
|
56
|
+
)
|
|
57
|
+
|
|
58
|
+
__all__ = ["Skills"]
|
|
59
|
+
|
|
60
|
+
ListSkillsHandler = Callable[[ServerRequestContext[Any, Any], ListSkillsParams], Awaitable[ListSkillsResult]]
|
|
61
|
+
GetSkillHandler = Callable[[ServerRequestContext[Any, Any], GetSkillParams], Awaitable[GetSkillResult]]
|
|
62
|
+
ReadDirectoryHandler = Callable[[ServerRequestContext[Any, Any], ReadDirectoryParams], Awaitable[ReadDirectoryResult]]
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
class Skills(Extension):
|
|
66
|
+
"""The Skills extension: serve `skills/list`, `skills/get`, and directory reads.
|
|
67
|
+
|
|
68
|
+
`list_skills` and `get_skill` are required; a server MUST answer both per
|
|
69
|
+
SEP-2640, whether or not a skill appears in the listing. `read_directory`
|
|
70
|
+
is optional — supplying it advertises the `directoryRead` capability
|
|
71
|
+
setting and serves `resources/directory/read`; omitting it advertises
|
|
72
|
+
neither. Handlers run per request, so a catalog that changes over time
|
|
73
|
+
(or is too large to enumerate) can return a partial or empty listing.
|
|
74
|
+
|
|
75
|
+
Handler error contract: raise `MCPError` to return a specific error to the
|
|
76
|
+
caller (e.g. `INVALID_PARAMS` from `get_skill` for a URI it doesn't serve).
|
|
77
|
+
A result that isn't SEP-2640 conformant is caught here and reported as
|
|
78
|
+
`INTERNAL_ERROR` — a server fault, not the caller's bad params.
|
|
79
|
+
"""
|
|
80
|
+
|
|
81
|
+
identifier = EXTENSION_ID
|
|
82
|
+
|
|
83
|
+
def __init__(
|
|
84
|
+
self,
|
|
85
|
+
*,
|
|
86
|
+
list_skills: ListSkillsHandler,
|
|
87
|
+
get_skill: GetSkillHandler,
|
|
88
|
+
read_directory: ReadDirectoryHandler | None = None,
|
|
89
|
+
) -> None:
|
|
90
|
+
self._list_skills = list_skills
|
|
91
|
+
self._get_skill = get_skill
|
|
92
|
+
self._read_directory = read_directory
|
|
93
|
+
|
|
94
|
+
def settings(self) -> dict[str, Any]:
|
|
95
|
+
return {"directoryRead": True} if self._read_directory is not None else {}
|
|
96
|
+
|
|
97
|
+
def methods(self) -> Sequence[MethodBinding]:
|
|
98
|
+
bindings = [
|
|
99
|
+
MethodBinding(METHOD_LIST, ListSkillsParams, self._handle_list),
|
|
100
|
+
MethodBinding(METHOD_GET, GetSkillParams, self._handle_get),
|
|
101
|
+
]
|
|
102
|
+
if self._read_directory is not None:
|
|
103
|
+
bindings.append(MethodBinding(METHOD_READ_DIRECTORY, ReadDirectoryParams, self._handle_read_directory))
|
|
104
|
+
return bindings
|
|
105
|
+
|
|
106
|
+
async def _handle_list(self, ctx: ServerRequestContext[Any, Any], params: ListSkillsParams) -> HandlerResult:
|
|
107
|
+
# `ListSkillsResult`/`Skill` self-validate on construction, so a handler that builds a
|
|
108
|
+
# non-conformant listing raises `ValidationError` here — a server fault, surfaced as an
|
|
109
|
+
# Internal error rather than the framework's default Invalid params for a bad body. The
|
|
110
|
+
# models stay mutable, so re-validate the outbound payload too: a handler that mutates a
|
|
111
|
+
# skill after building the result can't ship a non-conformant listing past this point.
|
|
112
|
+
try:
|
|
113
|
+
result = await self._list_skills(ctx, params)
|
|
114
|
+
ListSkillsResult.model_validate(result.model_dump())
|
|
115
|
+
except ValidationError:
|
|
116
|
+
raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result") from None
|
|
117
|
+
return _finalize_cacheable(result, ctx.protocol_version)
|
|
118
|
+
|
|
119
|
+
async def _handle_get(self, ctx: ServerRequestContext[Any, Any], params: GetSkillParams) -> HandlerResult:
|
|
120
|
+
_require_skill_md_uri(params.uri)
|
|
121
|
+
# `Skill` self-validates on construction, and the re-validation guards post-construction
|
|
122
|
+
# mutation, both as in `_handle_list`.
|
|
123
|
+
try:
|
|
124
|
+
result = await self._get_skill(ctx, params)
|
|
125
|
+
GetSkillResult.model_validate(result.model_dump())
|
|
126
|
+
except ValidationError:
|
|
127
|
+
raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result") from None
|
|
128
|
+
if result.skill.uri != params.uri:
|
|
129
|
+
raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result")
|
|
130
|
+
return _finalize_cacheable(result, ctx.protocol_version)
|
|
131
|
+
|
|
132
|
+
async def _handle_read_directory(
|
|
133
|
+
self, ctx: ServerRequestContext[Any, Any], params: ReadDirectoryParams
|
|
134
|
+
) -> HandlerResult:
|
|
135
|
+
assert self._read_directory is not None
|
|
136
|
+
_require_directory_uri(params.uri)
|
|
137
|
+
result = await self._read_directory(ctx, params)
|
|
138
|
+
try:
|
|
139
|
+
validate_directory_result(params.uri, result)
|
|
140
|
+
except ValueError:
|
|
141
|
+
raise MCPError(code=INTERNAL_ERROR, message="Handler returned an invalid result") from None
|
|
142
|
+
return result
|
|
143
|
+
|
|
144
|
+
|
|
145
|
+
def _require_skill_md_uri(uri: str) -> None:
|
|
146
|
+
try:
|
|
147
|
+
skill_name_from_uri(uri)
|
|
148
|
+
except ValueError as exc:
|
|
149
|
+
raise MCPError(code=INVALID_PARAMS, message=str(exc)) from exc
|
|
150
|
+
|
|
151
|
+
|
|
152
|
+
def _require_directory_uri(uri: str) -> None:
|
|
153
|
+
try:
|
|
154
|
+
parse_directory_uri(uri)
|
|
155
|
+
except ValueError as exc:
|
|
156
|
+
raise MCPError(code=INVALID_PARAMS, message=str(exc)) from exc
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
def _finalize_cacheable(result: CacheableResult, protocol_version: str) -> HandlerResult:
|
|
160
|
+
"""Gate SEP-2549's `ttlMs`/`cacheScope` to protocol version 2026-07-28+.
|
|
161
|
+
|
|
162
|
+
`skills/list` and `skills/get` are extension methods, so — unlike a core spec
|
|
163
|
+
method — the runner's per-version surface sieve never runs on their results;
|
|
164
|
+
nothing else strips these fields for a legacy connection. `CacheableResult`
|
|
165
|
+
defaults to `cache_scope="private"`, keeping user-specific entries out of shared caches.
|
|
166
|
+
"""
|
|
167
|
+
if protocol_version in MODERN_PROTOCOL_VERSIONS:
|
|
168
|
+
return result
|
|
169
|
+
dumped = result.model_dump(by_alias=True, mode="json", exclude_none=True)
|
|
170
|
+
dumped.pop("ttlMs", None)
|
|
171
|
+
dumped.pop("cacheScope", None)
|
|
172
|
+
return dumped
|