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.
Files changed (47) hide show
  1. cwl2mcp-0.1.0/.gitignore +40 -0
  2. cwl2mcp-0.1.0/CHANGELOG.md +29 -0
  3. cwl2mcp-0.1.0/LICENSE +4 -0
  4. cwl2mcp-0.1.0/PKG-INFO +95 -0
  5. cwl2mcp-0.1.0/README.md +59 -0
  6. cwl2mcp-0.1.0/docs/explanation/architecture.md +72 -0
  7. cwl2mcp-0.1.0/docs/explanation/index.md +4 -0
  8. cwl2mcp-0.1.0/docs/how-to/configure-output.md +39 -0
  9. cwl2mcp-0.1.0/docs/how-to/index.md +7 -0
  10. cwl2mcp-0.1.0/docs/how-to/install.md +32 -0
  11. cwl2mcp-0.1.0/docs/how-to/use-with-mcp.md +22 -0
  12. cwl2mcp-0.1.0/docs/how-to/use-with-oras.md +7 -0
  13. cwl2mcp-0.1.0/docs/index.md +20 -0
  14. cwl2mcp-0.1.0/docs/reference/api.md +57 -0
  15. cwl2mcp-0.1.0/docs/reference/crosswalk.md +138 -0
  16. cwl2mcp-0.1.0/docs/reference/hints_schema.html +218 -0
  17. cwl2mcp-0.1.0/docs/reference/index.md +11 -0
  18. cwl2mcp-0.1.0/docs/tutorials/first-steps.md +52 -0
  19. cwl2mcp-0.1.0/docs/tutorials/index.md +4 -0
  20. cwl2mcp-0.1.0/pyproject.toml +348 -0
  21. cwl2mcp-0.1.0/src/cwl2mcp/__about__.py +15 -0
  22. cwl2mcp-0.1.0/src/cwl2mcp/__init__.py +19 -0
  23. cwl2mcp-0.1.0/src/cwl2mcp/converter.py +142 -0
  24. cwl2mcp-0.1.0/src/cwl2mcp/mcp_hint.py +3342 -0
  25. cwl2mcp-0.1.0/src/cwl2mcp/mcp_tool.py +127 -0
  26. cwl2mcp-0.1.0/src/cwl2mcp/plugin.py +74 -0
  27. cwl2mcp-0.1.0/src/cwl2mcp/py.typed +0 -0
  28. cwl2mcp-0.1.0/stubs/transpiler_mate/runtime/cli.pyi +5 -0
  29. cwl2mcp-0.1.0/tests/examples/arguments.json +5 -0
  30. cwl2mcp-0.1.0/tests/examples/eoap.cwl +99 -0
  31. cwl2mcp-0.1.0/tests/fixtures/NOTICE.md +5 -0
  32. cwl2mcp-0.1.0/tests/fixtures/eoap/geojson.yaml +250 -0
  33. cwl2mcp-0.1.0/tests/fixtures/eoap/string_format.yaml +238 -0
  34. cwl2mcp-0.1.0/tests/fixtures/graph.cwl +48 -0
  35. cwl2mcp-0.1.0/tests/fixtures/minimal-tool.cwl +43 -0
  36. cwl2mcp-0.1.0/tests/fixtures/minimal-tool.mcp.json +25 -0
  37. cwl2mcp-0.1.0/tests/fixtures/typed-file.cwl +16 -0
  38. cwl2mcp-0.1.0/tests/fixtures/typed-report.yaml +9 -0
  39. cwl2mcp-0.1.0/tests/fixtures/types.cwl +54 -0
  40. cwl2mcp-0.1.0/tests/test_cli.py +94 -0
  41. cwl2mcp-0.1.0/tests/test_conversion.py +181 -0
  42. cwl2mcp-0.1.0/tests/test_cwl_compatibility.py +48 -0
  43. cwl2mcp-0.1.0/tests/test_file_conversion.py +52 -0
  44. cwl2mcp-0.1.0/tests/test_hint_schema.py +97 -0
  45. cwl2mcp-0.1.0/tests/test_plugin.py +74 -0
  46. cwl2mcp-0.1.0/tests/test_runtime_crosswalk.py +119 -0
  47. cwl2mcp-0.1.0/tests/test_tool_hint.py +130 -0
@@ -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
@@ -0,0 +1,4 @@
1
+ Pending license selection.
2
+
3
+ This repository scaffold was created without assuming a final project license.
4
+ Replace this file with the chosen license text before publishing or distributing releases.
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
+ [![PyPI - Version](https://img.shields.io/pypi/v/cwl2mcp.svg)](https://pypi.org/project/cwl2mcp)
40
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cwl2mcp.svg)](https://pypi.org/project/cwl2mcp)
41
+ [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/transpiler-mate/cwl2mcp/package.yaml?branch=develop&event=push&label=build&logo=githubactions)](https://github.com/transpiler-mate/cwl2mcp/actions/workflows/package.yaml?query=branch%3Adevelop)
42
+ [![Code coverage](https://img.shields.io/codecov/c/github/transpiler-mate/cwl2mcp/develop?logo=codecov)](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
+ [![Apache License, Version 2.0](https://img.shields.io/badge/license-Apache%20License%202.0-blue)](https://www.apache.org/licenses/LICENSE-2.0)
@@ -0,0 +1,59 @@
1
+ # cwl2mcp
2
+
3
+ [![PyPI - Version](https://img.shields.io/pypi/v/cwl2mcp.svg)](https://pypi.org/project/cwl2mcp)
4
+ [![PyPI - Python Version](https://img.shields.io/pypi/pyversions/cwl2mcp.svg)](https://pypi.org/project/cwl2mcp)
5
+ [![GitHub Actions Workflow Status](https://img.shields.io/github/actions/workflow/status/transpiler-mate/cwl2mcp/package.yaml?branch=develop&event=push&label=build&logo=githubactions)](https://github.com/transpiler-mate/cwl2mcp/actions/workflows/package.yaml?query=branch%3Adevelop)
6
+ [![Code coverage](https://img.shields.io/codecov/c/github/transpiler-mate/cwl2mcp/develop?logo=codecov)](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
+ [![Apache License, Version 2.0](https://img.shields.io/badge/license-Apache%20License%202.0-blue)](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,4 @@
1
+ # Explanation
2
+
3
+ Read [Architecture and data flow](architecture.md) for the boundaries between
4
+ the runtime, hint loader, schema converter, generated models, and MCP server.
@@ -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).