cwl2mcp 0.1.0__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.
- cwl2mcp-0.1.0/.gitignore +40 -0
- cwl2mcp-0.1.0/CHANGELOG.md +29 -0
- cwl2mcp-0.1.0/LICENSE +4 -0
- cwl2mcp-0.1.0/PKG-INFO +95 -0
- cwl2mcp-0.1.0/README.md +59 -0
- cwl2mcp-0.1.0/docs/explanation/architecture.md +72 -0
- cwl2mcp-0.1.0/docs/explanation/index.md +4 -0
- cwl2mcp-0.1.0/docs/how-to/configure-output.md +39 -0
- cwl2mcp-0.1.0/docs/how-to/index.md +7 -0
- cwl2mcp-0.1.0/docs/how-to/install.md +32 -0
- cwl2mcp-0.1.0/docs/how-to/use-with-mcp.md +22 -0
- cwl2mcp-0.1.0/docs/how-to/use-with-oras.md +7 -0
- cwl2mcp-0.1.0/docs/index.md +20 -0
- cwl2mcp-0.1.0/docs/reference/api.md +57 -0
- cwl2mcp-0.1.0/docs/reference/crosswalk.md +138 -0
- cwl2mcp-0.1.0/docs/reference/hints_schema.html +218 -0
- cwl2mcp-0.1.0/docs/reference/index.md +11 -0
- cwl2mcp-0.1.0/docs/tutorials/first-steps.md +52 -0
- cwl2mcp-0.1.0/docs/tutorials/index.md +4 -0
- cwl2mcp-0.1.0/pyproject.toml +348 -0
- cwl2mcp-0.1.0/src/cwl2mcp/__about__.py +15 -0
- cwl2mcp-0.1.0/src/cwl2mcp/__init__.py +19 -0
- cwl2mcp-0.1.0/src/cwl2mcp/converter.py +142 -0
- cwl2mcp-0.1.0/src/cwl2mcp/mcp_hint.py +3342 -0
- cwl2mcp-0.1.0/src/cwl2mcp/mcp_tool.py +127 -0
- cwl2mcp-0.1.0/src/cwl2mcp/plugin.py +74 -0
- cwl2mcp-0.1.0/src/cwl2mcp/py.typed +0 -0
- cwl2mcp-0.1.0/stubs/transpiler_mate/runtime/cli.pyi +5 -0
- cwl2mcp-0.1.0/tests/examples/arguments.json +5 -0
- cwl2mcp-0.1.0/tests/examples/eoap.cwl +99 -0
- cwl2mcp-0.1.0/tests/fixtures/NOTICE.md +5 -0
- cwl2mcp-0.1.0/tests/fixtures/eoap/geojson.yaml +250 -0
- cwl2mcp-0.1.0/tests/fixtures/eoap/string_format.yaml +238 -0
- cwl2mcp-0.1.0/tests/fixtures/graph.cwl +48 -0
- cwl2mcp-0.1.0/tests/fixtures/minimal-tool.cwl +43 -0
- cwl2mcp-0.1.0/tests/fixtures/minimal-tool.mcp.json +25 -0
- cwl2mcp-0.1.0/tests/fixtures/typed-file.cwl +16 -0
- cwl2mcp-0.1.0/tests/fixtures/typed-report.yaml +9 -0
- cwl2mcp-0.1.0/tests/fixtures/types.cwl +54 -0
- cwl2mcp-0.1.0/tests/test_cli.py +94 -0
- cwl2mcp-0.1.0/tests/test_conversion.py +181 -0
- cwl2mcp-0.1.0/tests/test_cwl_compatibility.py +48 -0
- cwl2mcp-0.1.0/tests/test_file_conversion.py +52 -0
- cwl2mcp-0.1.0/tests/test_hint_schema.py +97 -0
- cwl2mcp-0.1.0/tests/test_plugin.py +74 -0
- cwl2mcp-0.1.0/tests/test_runtime_crosswalk.py +119 -0
- cwl2mcp-0.1.0/tests/test_tool_hint.py +130 -0
cwl2mcp-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# Copyright {{ copyright_year }} {{ author_name }}
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
# Python
|
|
16
|
+
__pycache__/
|
|
17
|
+
*.py[cod]
|
|
18
|
+
*.egg-info/
|
|
19
|
+
.dist/
|
|
20
|
+
build/
|
|
21
|
+
dist/
|
|
22
|
+
.coverage
|
|
23
|
+
coverage.xml
|
|
24
|
+
.pytest_cache/
|
|
25
|
+
.mypy_cache/
|
|
26
|
+
.ruff_cache/
|
|
27
|
+
|
|
28
|
+
# Environments
|
|
29
|
+
.venv/
|
|
30
|
+
|
|
31
|
+
# MkDocs
|
|
32
|
+
site/
|
|
33
|
+
|
|
34
|
+
# Editors
|
|
35
|
+
.idea/
|
|
36
|
+
.vscode/
|
|
37
|
+
.DS_Store
|
|
38
|
+
|
|
39
|
+
.task
|
|
40
|
+
uv.lock
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to this project will be documented in this file.
|
|
4
|
+
|
|
5
|
+
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
|
6
|
+
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
7
|
+
|
|
8
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
### Changed
|
|
13
|
+
|
|
14
|
+
### Deprecated
|
|
15
|
+
|
|
16
|
+
### Removed
|
|
17
|
+
|
|
18
|
+
### Fixed
|
|
19
|
+
|
|
20
|
+
### Security
|
|
21
|
+
|
|
22
|
+
## [0.1.0]
|
|
23
|
+
|
|
24
|
+
### Added
|
|
25
|
+
|
|
26
|
+
- Inital released version.
|
|
27
|
+
|
|
28
|
+
[unreleased]: https://github.com/transpiler-mate/cwl2mcp/compare/v0.1.0...HEAD
|
|
29
|
+
[0.1.0]: https://github.com/transpiler-mate/cwl2mcp/releases/tag/v0.1.0
|
cwl2mcp-0.1.0/LICENSE
ADDED
cwl2mcp-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: cwl2mcp
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Derive MCP tool definitions from CWL processes
|
|
5
|
+
Project-URL: Homepage, https://github.com/Transpiler-Mate/cwl2mcp
|
|
6
|
+
Project-URL: Documentation, https://Transpiler-Mate.github.io/cwl2mcp/
|
|
7
|
+
Project-URL: Repository, https://github.com/Transpiler-Mate/cwl2mcp
|
|
8
|
+
Project-URL: Issues, https://github.com/Transpiler-Mate/cwl2mcp/issues
|
|
9
|
+
Project-URL: Changelog, https://github.com/Transpiler-Mate/cwl2mcp/blob/main/CHANGELOG.md
|
|
10
|
+
Author-email: Terradue srl <info@terradue.com>, Fabrice Brito <info@terradue.com>, Simone Tripodi <info@terradue.com>
|
|
11
|
+
License-Expression: Apache-2.0
|
|
12
|
+
License-File: LICENSE
|
|
13
|
+
Keywords: MCP,commonwl,cwl,transpiler-mate
|
|
14
|
+
Classifier: Development Status :: 4 - Beta
|
|
15
|
+
Classifier: Programming Language :: Python
|
|
16
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
20
|
+
Classifier: Programming Language :: Python :: 3.14
|
|
21
|
+
Classifier: Programming Language :: Python :: Implementation :: CPython
|
|
22
|
+
Classifier: Programming Language :: Python :: Implementation :: PyPy
|
|
23
|
+
Requires-Python: >=3.10
|
|
24
|
+
Requires-Dist: cwl2ogc==0.22.2
|
|
25
|
+
Requires-Dist: transpiler-mate-api<2,>=1.0.1
|
|
26
|
+
Provides-Extra: cli
|
|
27
|
+
Requires-Dist: transpiler-mate-runtime<2,>=1.0.0; extra == 'cli'
|
|
28
|
+
Provides-Extra: test
|
|
29
|
+
Requires-Dist: click<9,>=8.4.2; extra == 'test'
|
|
30
|
+
Requires-Dist: cwl-loader>=0.27.0; extra == 'test'
|
|
31
|
+
Requires-Dist: jsonschema[format-nongpl]<5,>=4.26; extra == 'test'
|
|
32
|
+
Requires-Dist: pytest-cov<8,>=7.1.0; extra == 'test'
|
|
33
|
+
Requires-Dist: pytest<10,>=9.1.1; extra == 'test'
|
|
34
|
+
Requires-Dist: transpiler-mate-runtime<2,>=1.0.0; extra == 'test'
|
|
35
|
+
Description-Content-Type: text/markdown
|
|
36
|
+
|
|
37
|
+
# cwl2mcp
|
|
38
|
+
|
|
39
|
+
[](https://pypi.org/project/cwl2mcp)
|
|
40
|
+
[](https://pypi.org/project/cwl2mcp)
|
|
41
|
+
[](https://github.com/transpiler-mate/cwl2mcp/actions/workflows/package.yaml?query=branch%3Adevelop)
|
|
42
|
+
[](https://app.codecov.io/gh/transpiler-mate/cwl2mcp/tree/develop)
|
|
43
|
+
|
|
44
|
+
A Transpiler Mate plugin deriving MCP tool definitions from parsed CWL v1.0–v1.2
|
|
45
|
+
processes. CWL is authoritative for value structure; EOAP `mcp:Tool` supplies
|
|
46
|
+
agent-facing metadata. This package does not execute CWL or run an MCP server.
|
|
47
|
+
|
|
48
|
+
## Installation and use
|
|
49
|
+
|
|
50
|
+
```bash
|
|
51
|
+
python -m pip install './cwl2mcp[cli]'
|
|
52
|
+
transpiler-mate cwl2mcp --output tool.json 'workflow.cwl#main'
|
|
53
|
+
transpiler-mate cwl2mcp --output tools.json workflow.cwl
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The current runtime requires document-level schema.org application metadata
|
|
57
|
+
(name, description, dateCreated, license, softwareVersion, softwareHelp, publisher
|
|
58
|
+
and author) before invoking plugins. The complete example and graph fixture show
|
|
59
|
+
this metadata. The Python API does not require it.
|
|
60
|
+
|
|
61
|
+
The runtime resolves imports and selects processes. A `#process` selection writes
|
|
62
|
+
one MCP Tool object. Without a fragment, output is an object mapping the runtime's
|
|
63
|
+
process IDs to tool definitions, including workflows, CommandLineTools and
|
|
64
|
+
ExpressionTools. This mapping is an export container, not an MCP `tools/list`
|
|
65
|
+
response. JSON keys are sorted, output ends with a newline, and parent directories
|
|
66
|
+
are created. Conversion completes before an existing file is overwritten.
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from cwl_utils.parser import load_document_by_uri
|
|
70
|
+
from cwl2mcp.converter import to_mcp_tool
|
|
71
|
+
|
|
72
|
+
process = load_document_by_uri('file:///absolute/path/workflow.cwl')
|
|
73
|
+
# Select one process first if the loader returned a list.
|
|
74
|
+
tool = to_mcp_tool(process)
|
|
75
|
+
print(tool.model_dump_json(by_alias=True, exclude_none=True, indent=2))
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Documentation
|
|
79
|
+
|
|
80
|
+
The documentation follows Diátaxis and is published at
|
|
81
|
+
<https://Transpiler-Mate.github.io/cwl2oci/>:
|
|
82
|
+
|
|
83
|
+
- tutorials for guided learning;
|
|
84
|
+
- how-to guides for specific tasks;
|
|
85
|
+
- reference for the exact plugin and output contracts;
|
|
86
|
+
- explanation for architecture and design context.
|
|
87
|
+
|
|
88
|
+
## Contributing
|
|
89
|
+
|
|
90
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Submit issues at
|
|
91
|
+
<https://github.com/Transpiler-Mate/cwl2oci/issues>.
|
|
92
|
+
|
|
93
|
+
## License
|
|
94
|
+
|
|
95
|
+
[](https://www.apache.org/licenses/LICENSE-2.0)
|
cwl2mcp-0.1.0/README.md
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# cwl2mcp
|
|
2
|
+
|
|
3
|
+
[](https://pypi.org/project/cwl2mcp)
|
|
4
|
+
[](https://pypi.org/project/cwl2mcp)
|
|
5
|
+
[](https://github.com/transpiler-mate/cwl2mcp/actions/workflows/package.yaml?query=branch%3Adevelop)
|
|
6
|
+
[](https://app.codecov.io/gh/transpiler-mate/cwl2mcp/tree/develop)
|
|
7
|
+
|
|
8
|
+
A Transpiler Mate plugin deriving MCP tool definitions from parsed CWL v1.0–v1.2
|
|
9
|
+
processes. CWL is authoritative for value structure; EOAP `mcp:Tool` supplies
|
|
10
|
+
agent-facing metadata. This package does not execute CWL or run an MCP server.
|
|
11
|
+
|
|
12
|
+
## Installation and use
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
python -m pip install './cwl2mcp[cli]'
|
|
16
|
+
transpiler-mate cwl2mcp --output tool.json 'workflow.cwl#main'
|
|
17
|
+
transpiler-mate cwl2mcp --output tools.json workflow.cwl
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The current runtime requires document-level schema.org application metadata
|
|
21
|
+
(name, description, dateCreated, license, softwareVersion, softwareHelp, publisher
|
|
22
|
+
and author) before invoking plugins. The complete example and graph fixture show
|
|
23
|
+
this metadata. The Python API does not require it.
|
|
24
|
+
|
|
25
|
+
The runtime resolves imports and selects processes. A `#process` selection writes
|
|
26
|
+
one MCP Tool object. Without a fragment, output is an object mapping the runtime's
|
|
27
|
+
process IDs to tool definitions, including workflows, CommandLineTools and
|
|
28
|
+
ExpressionTools. This mapping is an export container, not an MCP `tools/list`
|
|
29
|
+
response. JSON keys are sorted, output ends with a newline, and parent directories
|
|
30
|
+
are created. Conversion completes before an existing file is overwritten.
|
|
31
|
+
|
|
32
|
+
```python
|
|
33
|
+
from cwl_utils.parser import load_document_by_uri
|
|
34
|
+
from cwl2mcp.converter import to_mcp_tool
|
|
35
|
+
|
|
36
|
+
process = load_document_by_uri('file:///absolute/path/workflow.cwl')
|
|
37
|
+
# Select one process first if the loader returned a list.
|
|
38
|
+
tool = to_mcp_tool(process)
|
|
39
|
+
print(tool.model_dump_json(by_alias=True, exclude_none=True, indent=2))
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
## Documentation
|
|
43
|
+
|
|
44
|
+
The documentation follows Diátaxis and is published at
|
|
45
|
+
<https://Transpiler-Mate.github.io/cwl2oci/>:
|
|
46
|
+
|
|
47
|
+
- tutorials for guided learning;
|
|
48
|
+
- how-to guides for specific tasks;
|
|
49
|
+
- reference for the exact plugin and output contracts;
|
|
50
|
+
- explanation for architecture and design context.
|
|
51
|
+
|
|
52
|
+
## Contributing
|
|
53
|
+
|
|
54
|
+
See [CONTRIBUTING.md](CONTRIBUTING.md). Submit issues at
|
|
55
|
+
<https://github.com/Transpiler-Mate/cwl2oci/issues>.
|
|
56
|
+
|
|
57
|
+
## License
|
|
58
|
+
|
|
59
|
+
[](https://www.apache.org/licenses/LICENSE-2.0)
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
# Architecture and data flow
|
|
2
|
+
|
|
3
|
+
```text
|
|
4
|
+
transpiler-mate-runtime
|
|
5
|
+
resolves CWL, prepares metadata, selects processes, validates options
|
|
6
|
+
|
|
|
7
|
+
v
|
|
8
|
+
cwl2mcp plugin
|
|
9
|
+
loads the first matching Tool hint
|
|
10
|
+
delegates input/output schema conversion to cwl2ogc
|
|
11
|
+
constructs a generated McpTool model
|
|
12
|
+
serializes one Tool or a process-ID mapping
|
|
13
|
+
|
|
|
14
|
+
v
|
|
15
|
+
tools.json
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
The runtime owns document loading and plugin discovery. The
|
|
19
|
+
[plugin](../../src/cwl2mcp/plugin.py) writes JSON; the
|
|
20
|
+
[converter](../../src/cwl2mcp/converter.py) returns a Tool without performing file
|
|
21
|
+
I/O. An [MCP server](../how-to/use-with-mcp.md) must supply execution and protocol
|
|
22
|
+
handling separately.
|
|
23
|
+
|
|
24
|
+
## Two schema boundaries
|
|
25
|
+
|
|
26
|
+
[schemas/hints.yaml](../../schemas/hints.yaml) is a Schema Salad extension of
|
|
27
|
+
CWL. Its generated [mcp_hint.py](../../src/cwl2mcp/mcp_hint.py) validates custom
|
|
28
|
+
hint mappings. Imported CWL and Salad classes are reused from `cwl-utils` and
|
|
29
|
+
`schema-salad`. The generator uses `Any` constructor annotations; validation
|
|
30
|
+
occurs in `fromDoc()`, not in those annotations.
|
|
31
|
+
|
|
32
|
+
[schemas/mcp-tool.yaml](../../schemas/mcp-tool.yaml) defines the project's MCP
|
|
33
|
+
Tool authoring profile. Its generated [mcp_tool.py](../../src/cwl2mcp/mcp_tool.py)
|
|
34
|
+
contains Pydantic models. This profile is distinct from the official protocol
|
|
35
|
+
schema. Constructing `McpTool` validates model fields but does not execute the
|
|
36
|
+
embedded JSON Schemas against arguments or results.
|
|
37
|
+
|
|
38
|
+
The converter preserves the literal hint name after Salad validation because
|
|
39
|
+
Salad's shared `name` predicate otherwise applies document-relative identifier
|
|
40
|
+
expansion. Input/output schema conversion uses the pinned `cwl2ogc` dependency;
|
|
41
|
+
its generated schemas are copied without a separate artifact-receipt layer.
|
|
42
|
+
|
|
43
|
+
## Regeneration
|
|
44
|
+
|
|
45
|
+
Run the configured tasks from the repository root:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
task generate_mpc_hint
|
|
49
|
+
task generate_mpc_tool
|
|
50
|
+
task generate_schema_doc
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
The `mpc` spelling is the current task name. The Tool task invokes the shared
|
|
54
|
+
Terradue `json:create_models` task; the hint tasks use Schema Salad. See
|
|
55
|
+
[Taskfile.yaml](../../Taskfile.yaml). Change source schemas and regenerate rather
|
|
56
|
+
than editing generated classes or the [HTML hint reference](../reference/hints_schema.html).
|
|
57
|
+
|
|
58
|
+
## Verification
|
|
59
|
+
|
|
60
|
+
[Runtime integration tests](../../tests/test_runtime_crosswalk.py) exercise real
|
|
61
|
+
CLI discovery and exported JSON. Assertions cover the current scalar URI/DateTime projection, metadata keys,
|
|
62
|
+
selected-process and document exports, and deterministic serialization. The
|
|
63
|
+
[hint schema tests](../../tests/test_hint_schema.py) cover the title and nested
|
|
64
|
+
annotation handling. Run `task quality:pre-commit:run` for the complete quality gate. The test
|
|
65
|
+
environments install JSON Schema format validators so malformed URI and
|
|
66
|
+
date-time values are rejected.
|
|
67
|
+
|
|
68
|
+
The hint-generation task applies reviewed Ruff rewrites and formatting after
|
|
69
|
+
code generation. Generated loader complexity and schema-shaped signatures have
|
|
70
|
+
file-specific Ruff exceptions; the upstream abstract-loader registration has a
|
|
71
|
+
narrow mypy exception. Other checks remain enabled. See the comments in
|
|
72
|
+
[pyproject.toml](../../pyproject.toml) for these generated-code boundaries.
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Configure output and metadata keys
|
|
2
|
+
|
|
3
|
+
From the repository root:
|
|
4
|
+
|
|
5
|
+
```sh
|
|
6
|
+
transpiler-mate cwl2mcp \
|
|
7
|
+
--output build/tool.json \
|
|
8
|
+
--meta-prefix org.example/mcp \
|
|
9
|
+
tests/examples/eoap.cwl#main
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
`--output` defaults to `tools.json`. Missing parent directories are created;
|
|
13
|
+
existing files are replaced after successful conversion. JSON keys are sorted,
|
|
14
|
+
indentation is two spaces, and the file ends with a newline.
|
|
15
|
+
|
|
16
|
+
`--meta-prefix` defaults to `io.github.transpiler-mate/cwl2mcp`, users can customize it keeping in mind MCP’s `_meta` naming rules distinguish two parts:
|
|
17
|
+
|
|
18
|
+
| Part | Rules |
|
|
19
|
+
|---|---|
|
|
20
|
+
| Optional prefix, before `/` | Dot-separated labels; reverse DNS is recommended. Labels start with a letter, end with a letter or digit, and may contain internal hyphens. |
|
|
21
|
+
| Name, after `/` | May contain alphanumerics, hyphens, underscores, and dots. A nonempty name must start and end with an alphanumeric character. |
|
|
22
|
+
|
|
23
|
+
The command above
|
|
24
|
+
produces these custom keys when their values are nonempty:
|
|
25
|
+
|
|
26
|
+
- `org.example/mcp-aliases`
|
|
27
|
+
- `org.example/mcp-use-when`
|
|
28
|
+
- `org.example/mcp-do-not-use-when`
|
|
29
|
+
|
|
30
|
+
The suffixes use underscores. Empty lists and empty strings are omitted.
|
|
31
|
+
|
|
32
|
+
Use `tests/examples/eoap.cwl#main` to export one Tool. Omit `#main` to export an
|
|
33
|
+
object mapping process IDs to Tool definitions. This mapping is not an MCP
|
|
34
|
+
`tools/list` response.
|
|
35
|
+
|
|
36
|
+
For host integrations, construct `Cwl2McpOptions(output=Path("build/tool.json"),
|
|
37
|
+
meta_prefix="org.example/mcp")` and pass it to the plugin registration's
|
|
38
|
+
`execute(context, options)` method. See the [API reference](../reference/api.md)
|
|
39
|
+
and [crosswalk](../reference/crosswalk.md).
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# How-to guides
|
|
2
|
+
|
|
3
|
+
- [Install the plugin and runtime](install.md).
|
|
4
|
+
- [Configure output and metadata keys](configure-output.md).
|
|
5
|
+
- [Use definitions in an MCP server](use-with-mcp.md).
|
|
6
|
+
|
|
7
|
+
For a complete first run, follow the [tutorial](../tutorials/first-steps.md).
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
# Install cwl2mcp
|
|
2
|
+
|
|
3
|
+
Use Python 3.10 or newer. From this repository's root, install the plugin and its
|
|
4
|
+
runtime extra into the same environment:
|
|
5
|
+
|
|
6
|
+
```sh
|
|
7
|
+
python -m venv .venv
|
|
8
|
+
. .venv/bin/activate
|
|
9
|
+
python -m pip install '.[cli]'
|
|
10
|
+
transpiler-mate cwl2mcp --help
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
For editable development, use `python -m pip install -e '.[cli,test]'` instead.
|
|
14
|
+
Dependencies are declared in [pyproject.toml](../../pyproject.toml); installation
|
|
15
|
+
normally requires network access.
|
|
16
|
+
|
|
17
|
+
The package installs a plugin entry point, not a standalone `cwl2mcp` executable.
|
|
18
|
+
`transpiler-mate-runtime` provides the `transpiler-mate` command.
|
|
19
|
+
|
|
20
|
+
Verify the registration and the imported source:
|
|
21
|
+
|
|
22
|
+
```sh
|
|
23
|
+
python -c "from cwl2mcp.plugin import cwl2mcp; print(cwl2mcp.name)"
|
|
24
|
+
python -c "import cwl2mcp; print(cwl2mcp.__file__)"
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
The first command prints `cwl2mcp`. If a traceback points to an older installed
|
|
28
|
+
copy, reinstall with the interpreter belonging to that CLI environment or use
|
|
29
|
+
an editable installation. Regenerating models in a checkout does not update a
|
|
30
|
+
separate installation automatically.
|
|
31
|
+
|
|
32
|
+
Continue with the [first tool tutorial](../tutorials/first-steps.md).
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Use definitions in an MCP server
|
|
2
|
+
|
|
3
|
+
Generate a selected Tool using the [runtime tutorial](../tutorials/first-steps.md).
|
|
4
|
+
An MCP server can then use that definition when advertising its tools. For a
|
|
5
|
+
whole-document export, take the values of the process-ID mapping first.
|
|
6
|
+
The plugin's JSON is not itself a protocol response.
|
|
7
|
+
|
|
8
|
+
The server is responsible for listing tools, handling calls, validating
|
|
9
|
+
arguments, executing the workflow, and returning results. See the
|
|
10
|
+
[MCP tools specification](https://modelcontextprotocol.io/specification/2025-11-25/server/tools)
|
|
11
|
+
for the protocol messages and result contract.
|
|
12
|
+
|
|
13
|
+
Validate incoming arguments against the generated `inputSchema` and returned
|
|
14
|
+
objects against `outputSchema`. Enable a JSON Schema format checker if URI and
|
|
15
|
+
date-time formats should be enforced. The generated EOAP example expects scalar
|
|
16
|
+
URI and timestamp strings; an adapter must map them back to CWL record-shaped
|
|
17
|
+
values if its runner requires those records. The plugin supplies no execution
|
|
18
|
+
adapter.
|
|
19
|
+
|
|
20
|
+
Tool annotations describe intended behavior and do not grant authorization.
|
|
21
|
+
Discovery aliases in `_meta` do not register additional callable names.
|
|
22
|
+
See the [crosswalk](../reference/crosswalk.md) for the exact exported shapes.
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# MCP output and OCI annotations
|
|
2
|
+
|
|
3
|
+
`cwl2mcp` produces MCP Tool definitions, not ORAS manifest annotations. The old
|
|
4
|
+
ORAS instructions on this page belonged to a different plugin.
|
|
5
|
+
|
|
6
|
+
Continue with [using definitions in an MCP server](use-with-mcp.md),
|
|
7
|
+
[output configuration](configure-output.md), or the [documentation index](../index.md).
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# CWL to MCP
|
|
2
|
+
|
|
3
|
+
`cwl2mcp` converts parsed CWL processes into MCP Tool definitions and writes them
|
|
4
|
+
as JSON through `transpiler-mate-runtime`. It does not execute CWL or start an
|
|
5
|
+
MCP server.
|
|
6
|
+
|
|
7
|
+
## Start here
|
|
8
|
+
|
|
9
|
+
- [Install the plugin and runtime](how-to/install.md).
|
|
10
|
+
- [Generate your first tool definition](tutorials/first-steps.md).
|
|
11
|
+
- [Configure output and metadata keys](how-to/configure-output.md).
|
|
12
|
+
- [Use the exported definitions in an MCP server](how-to/use-with-mcp.md).
|
|
13
|
+
- Look up the [CWL-to-MCP crosswalk](reference/crosswalk.md),
|
|
14
|
+
[Python and plugin API](reference/api.md), and
|
|
15
|
+
[generated hint reference](reference/hints_schema.html).
|
|
16
|
+
- Read about the [architecture](explanation/architecture.md).
|
|
17
|
+
|
|
18
|
+
Python 3.10 or newer is required. The runtime discovers the installed plugin via
|
|
19
|
+
its `transpiler_mate.plugins` entry point. See the
|
|
20
|
+
[project configuration](../pyproject.toml) for dependencies and quality checks.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
# Plugin and Python API
|
|
2
|
+
|
|
3
|
+
## Registration and options
|
|
4
|
+
|
|
5
|
+
The [plugin implementation](../../src/cwl2mcp/plugin.py) registers
|
|
6
|
+
`cwl2mcp.plugin:cwl2mcp` as `cwl2mcp` in the `transpiler_mate.plugins` group.
|
|
7
|
+
The decorated object is a plugin registration; a host invokes
|
|
8
|
+
`cwl2mcp.execute(context, options)`.
|
|
9
|
+
|
|
10
|
+
`Cwl2McpOptions` rejects unknown fields:
|
|
11
|
+
|
|
12
|
+
| Option | Python type | Default |
|
|
13
|
+
|---|---|---|
|
|
14
|
+
| `output` | `pathlib.Path` | `tools.json` |
|
|
15
|
+
| `meta_prefix` | `str` | `io.github.transpiler-mate/cwl2mcp` |
|
|
16
|
+
|
|
17
|
+
The runtime exposes these as `--output` and `--meta-prefix`. See
|
|
18
|
+
[output configuration](../how-to/configure-output.md).
|
|
19
|
+
|
|
20
|
+
When `context.process_id` is set, the plugin converts `context.resolved_process`.
|
|
21
|
+
Otherwise it converts each process in `context.document` and writes a mapping
|
|
22
|
+
keyed by the runtime's process IDs. The runtime also prepares software application
|
|
23
|
+
metadata, but this plugin reads tool metadata from the CWL process and hint.
|
|
24
|
+
|
|
25
|
+
## Direct conversion
|
|
26
|
+
|
|
27
|
+
```python
|
|
28
|
+
from pathlib import Path
|
|
29
|
+
from cwl_utils.parser import Process, load_document_by_uri
|
|
30
|
+
from cwl2mcp.converter import to_mcp_tool
|
|
31
|
+
|
|
32
|
+
process = load_document_by_uri(
|
|
33
|
+
Path("tests/examples/eoap.cwl").resolve().as_uri() + "#main"
|
|
34
|
+
)
|
|
35
|
+
assert isinstance(process, Process)
|
|
36
|
+
tool = to_mcp_tool(process, meta_prefix="example.org/mcp")
|
|
37
|
+
payload = tool.model_dump(by_alias=True, exclude_none=True)
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
`to_mcp_tool(process, meta_prefix)` returns the generated `McpTool` Pydantic model
|
|
41
|
+
and does not write files. `meta_prefix` is required in the Python API.
|
|
42
|
+
`get_tool_hint(process)` in [converter.py](../../src/cwl2mcp/converter.py) returns
|
|
43
|
+
the first matching `Tool` hint, or `None`. It accepts parsed hints and validates
|
|
44
|
+
mapping hints using the generated Salad model.
|
|
45
|
+
|
|
46
|
+
## Errors
|
|
47
|
+
|
|
48
|
+
- Malformed hint mappings raise Schema Salad `ValidationException`.
|
|
49
|
+
- Invalid tool fields raise Pydantic `ValidationError` during conversion.
|
|
50
|
+
- Schema conversion can raise errors from `cwl2ogc`.
|
|
51
|
+
- The plugin wraps conversion-time `ValueError` as `PluginFailureError`.
|
|
52
|
+
Salad validation errors are not wrapped by that handler.
|
|
53
|
+
- File-system `OSError` is wrapped as `PluginExecutionError`.
|
|
54
|
+
|
|
55
|
+
Conversion finishes before the destination is opened. Parent directories are
|
|
56
|
+
created. Writing is not an atomic replacement: an I/O failure after opening can
|
|
57
|
+
leave a partial file. See the [crosswalk](crosswalk.md) for serialization details.
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# CWL to MCP crosswalk
|
|
2
|
+
|
|
3
|
+
This page describes the current [converter](../../src/cwl2mcp/converter.py) and
|
|
4
|
+
[export plugin](../../src/cwl2mcp/plugin.py). The plugin generates Tool definitions;
|
|
5
|
+
it does not execute CWL, provide an MCP server, or generate artifact receipts.
|
|
6
|
+
|
|
7
|
+
## Process fields
|
|
8
|
+
|
|
9
|
+
| CWL source | Resulting JSON | Current mapping |
|
|
10
|
+
|---|---|---|
|
|
11
|
+
| Nonempty `mcp:Tool.name`, otherwise process `id` | `name` | A mapping hint's literal name is preserved. The fallback process ID is not normalized. |
|
|
12
|
+
| Nonempty `mcp:Tool.title`, otherwise `label` | `title` | Omitted when neither supplies a nonempty value. |
|
|
13
|
+
| Nonempty process `doc` | `description` | Passed directly to the model. There is no hint description override or appended guidance. |
|
|
14
|
+
| `inputs` | `inputSchema` | Result of `BaseCWLtypes2OGCConverter.get_inputs_json_schema()`. |
|
|
15
|
+
| `outputs` | `outputSchema` | Result of `BaseCWLtypes2OGCConverter.get_outputs_json_schema()`. |
|
|
16
|
+
| `mcp:Tool.annotations` | `annotations` | Copies all four behavioral booleans, including `false`. Omitted when no hint is found. |
|
|
17
|
+
| Nonempty `mcp:Tool.aliases` | `_meta["<prefix>-aliases"]` | Preserves the list. |
|
|
18
|
+
| Nonempty `mcp:Tool.useWhen` | `_meta["<prefix>-use_when"]` | Preserves the string without changing `description`. |
|
|
19
|
+
| Nonempty `mcp:Tool.doNotUseWhen` | `_meta["<prefix>-do_not_use_when"]` | Preserves the string without changing `description`. |
|
|
20
|
+
|
|
21
|
+
The default prefix is `transpiler-mate.github.io/cwl2mcp`; configure it with
|
|
22
|
+
`--meta-prefix`. The converter always supplies `_meta`, even when it is `{}`.
|
|
23
|
+
See [output configuration](../how-to/configure-output.md).
|
|
24
|
+
|
|
25
|
+
The generated [MCP model](../../src/cwl2mcp/mcp_tool.py) requires a name of 1–128
|
|
26
|
+
ASCII letters, digits, underscores, dots, or hyphens. A URI-valued fallback ID can
|
|
27
|
+
therefore fail validation. Supply an explicit callable name in the hint.
|
|
28
|
+
`description` must be a string; lists of CWL documentation paragraphs are not
|
|
29
|
+
joined by the converter and can fail model validation.
|
|
30
|
+
|
|
31
|
+
## Hint selection and validation
|
|
32
|
+
|
|
33
|
+
Only `process.hints` is searched. The first matching Tool is used; later matches
|
|
34
|
+
are not rejected. `requirements` is not searched for MCP hints. Class names may
|
|
35
|
+
be full URIs or use any process namespace prefix bound to
|
|
36
|
+
`https://eoap.github.io/cwl-mcp-extension#`.
|
|
37
|
+
|
|
38
|
+
A complete hint for the [EOAP example](../../tests/examples/eoap.cwl) is:
|
|
39
|
+
|
|
40
|
+
```yaml
|
|
41
|
+
hints:
|
|
42
|
+
agent:Tool:
|
|
43
|
+
name: inspect_source
|
|
44
|
+
title: Inspect Source
|
|
45
|
+
aliases: [inspect a spatial source]
|
|
46
|
+
useWhen: the user needs to inspect a spatial source.
|
|
47
|
+
doNotUseWhen: the user needs data modification.
|
|
48
|
+
annotations:
|
|
49
|
+
readOnlyHint: true
|
|
50
|
+
destructiveHint: false
|
|
51
|
+
idempotentHint: true
|
|
52
|
+
openWorldHint: false
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
The document must bind `agent` to the extension namespace. See the
|
|
56
|
+
[hint source schema](../../schemas/hints.yaml) and
|
|
57
|
+
[generated hint reference](hints_schema.html) for field definitions.
|
|
58
|
+
`title` is optional. `aliases`, `useWhen`, `doNotUseWhen`, `annotations`, and all
|
|
59
|
+
four annotation booleans are required by the current mapping loader; empty
|
|
60
|
+
aliases/guidance values are allowed but omitted from `_meta`. The schema declares
|
|
61
|
+
`name` as an identifier string; Salad can synthesize a missing identifier, so an
|
|
62
|
+
explicit MCP-compatible name should always be provided.
|
|
63
|
+
|
|
64
|
+
`annotations.class` may be omitted. If supplied, it must identify `Annotations`.
|
|
65
|
+
`Tool.description` is not a declared hint field and is rejected. Unknown
|
|
66
|
+
unqualified fields and invalid field values raise validation errors. Already
|
|
67
|
+
constructed Tool instances are returned unchanged rather than revalidated.
|
|
68
|
+
|
|
69
|
+
## Parameter schemas and EOAP conversion
|
|
70
|
+
|
|
71
|
+
The project pins `cwl2ogc` in [pyproject.toml](../../pyproject.toml). Its schemas
|
|
72
|
+
are copied directly into the Tool; this plugin does not add parameter titles,
|
|
73
|
+
join parameter documentation, register extra formats, or provide a custom File
|
|
74
|
+
receipt schema. Inspect the emitted schema for the resulting types, defaults,
|
|
75
|
+
required fields, and definitions.
|
|
76
|
+
|
|
77
|
+
For the current EOAP example, the input schema contains definitions equivalent
|
|
78
|
+
to these excerpts:
|
|
79
|
+
|
|
80
|
+
```json
|
|
81
|
+
{
|
|
82
|
+
"source": {"type": "string", "format": "uri"},
|
|
83
|
+
"timestamp": {"type": "string", "format": "date-time"}
|
|
84
|
+
}
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Properties refer to them through `$ref` values such as `#/$defs/source`.
|
|
88
|
+
URI and DateTime records are flattened into scalar strings. The GeoJSON Point
|
|
89
|
+
remains an object requiring `type`, `coordinates`, and `bbox`. Its coordinate and
|
|
90
|
+
bounding-box values are arrays of numbers. The output schema uses corresponding
|
|
91
|
+
`source_result`, `timestamp_result`, and `point_result` fields.
|
|
92
|
+
|
|
93
|
+
Thus the [CWL arguments fixture](../../tests/examples/arguments.json), which
|
|
94
|
+
wraps URI and timestamp values in `{"value": ...}`, needs adaptation before it
|
|
95
|
+
can satisfy the exported MCP input schema. See the
|
|
96
|
+
[tutorial](../tutorials/first-steps.md) for a matching MCP argument object.
|
|
97
|
+
Format validation requires a format-aware JSON Schema validator.
|
|
98
|
+
|
|
99
|
+
## Example metadata output
|
|
100
|
+
|
|
101
|
+
The EOAP example produces this metadata excerpt, alongside its input and output
|
|
102
|
+
schemas:
|
|
103
|
+
|
|
104
|
+
```json
|
|
105
|
+
{
|
|
106
|
+
"name": "inspect_source",
|
|
107
|
+
"title": "Inspect Source",
|
|
108
|
+
"description": "Return a source URI, timestamp and GeoJSON point without changing their records.",
|
|
109
|
+
"annotations": {
|
|
110
|
+
"readOnlyHint": true,
|
|
111
|
+
"destructiveHint": false,
|
|
112
|
+
"idempotentHint": true,
|
|
113
|
+
"openWorldHint": false
|
|
114
|
+
},
|
|
115
|
+
"_meta": {
|
|
116
|
+
"transpiler-mate.github.io/cwl2mcp-aliases": ["inspect a spatial source"],
|
|
117
|
+
"transpiler-mate.github.io/cwl2mcp-use_when": "the user needs to inspect a spatial source.",
|
|
118
|
+
"transpiler-mate.github.io/cwl2mcp-do_not_use_when": "the user needs data modification."
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
## Export container
|
|
124
|
+
|
|
125
|
+
| CLI source | Export |
|
|
126
|
+
|---|---|
|
|
127
|
+
| `tests/examples/eoap.cwl#main` | One MCP Tool object. |
|
|
128
|
+
| `tests/examples/eoap.cwl` | A mapping such as `{"main": <Tool>}`. |
|
|
129
|
+
|
|
130
|
+
JSON aliases are used and fields whose value is `None` are omitted. Keys are
|
|
131
|
+
sorted, indentation is two spaces, and output ends with a newline. The plugin
|
|
132
|
+
creates parent directories and completes conversion before opening the output
|
|
133
|
+
file. It does not perform an atomic file replacement.
|
|
134
|
+
|
|
135
|
+
The runtime requires software application metadata before plugin execution;
|
|
136
|
+
the direct [Python conversion API](api.md) only needs a parsed process and a
|
|
137
|
+
metadata prefix. A whole-document export is a plugin container, not an MCP
|
|
138
|
+
`tools/list` response. See [using definitions in MCP](../how-to/use-with-mcp.md).
|