mcpc-cli 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.
- mcpc_cli-0.1.0/PKG-INFO +83 -0
- mcpc_cli-0.1.0/README.md +66 -0
- mcpc_cli-0.1.0/mcpc/__init__.py +3 -0
- mcpc_cli-0.1.0/mcpc/__main__.py +5 -0
- mcpc_cli-0.1.0/mcpc/cli.py +172 -0
- mcpc_cli-0.1.0/mcpc/init.py +300 -0
- mcpc_cli-0.1.0/mcpc/pack.py +124 -0
- mcpc_cli-0.1.0/mcpc/test.py +311 -0
- mcpc_cli-0.1.0/mcpc/unpack.py +103 -0
- mcpc_cli-0.1.0/mcpc/validate.py +537 -0
- mcpc_cli-0.1.0/mcpc_cli.egg-info/PKG-INFO +83 -0
- mcpc_cli-0.1.0/mcpc_cli.egg-info/SOURCES.txt +15 -0
- mcpc_cli-0.1.0/mcpc_cli.egg-info/dependency_links.txt +1 -0
- mcpc_cli-0.1.0/mcpc_cli.egg-info/entry_points.txt +2 -0
- mcpc_cli-0.1.0/mcpc_cli.egg-info/top_level.txt +1 -0
- mcpc_cli-0.1.0/pyproject.toml +29 -0
- mcpc_cli-0.1.0/setup.cfg +4 -0
mcpc_cli-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: mcpc-cli
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: CLI for the MCP Contract specification — validate, inspect, and manage contract bundles.
|
|
5
|
+
Author: Joe Fullerton
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://mcpcontracts.com
|
|
8
|
+
Project-URL: Repository, https://github.com/jmfullerton96/mcp-contract
|
|
9
|
+
Project-URL: Specification, https://github.com/jmfullerton96/mcp-contract/blob/main/SPEC.md
|
|
10
|
+
Keywords: mcp,ai,llm,composability,json-schema,workflow
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3
|
|
14
|
+
Classifier: Topic :: Software Development :: Libraries
|
|
15
|
+
Requires-Python: >=3.9
|
|
16
|
+
Description-Content-Type: text/markdown
|
|
17
|
+
|
|
18
|
+
# MCP Contract
|
|
19
|
+
|
|
20
|
+
[](https://github.com/jmfullerton96/mcp-contract/actions/workflows/ci.yml)
|
|
21
|
+
[](SPEC.md)
|
|
22
|
+
[](LICENSE)
|
|
23
|
+
[](schema/mcp-contract.schema.json)
|
|
24
|
+
[](https://mcpcontracts.com)
|
|
25
|
+
|
|
26
|
+
A specification for composable AI workflow bundles with typed contracts between layers.
|
|
27
|
+
|
|
28
|
+
## The Problem
|
|
29
|
+
|
|
30
|
+
MCP servers are monoliths. If you want to use one server's reasoning prompts with another server's tools and a third server's UI, you fork all three. MCP Contract fixes this by separating bundles into independently authored, versioned, and composable layers.
|
|
31
|
+
|
|
32
|
+
## The Layers
|
|
33
|
+
|
|
34
|
+
| Layer | Role | Analogy |
|
|
35
|
+
|---|---|---|
|
|
36
|
+
| **Prompts** | Reasoning logic | Source code |
|
|
37
|
+
| **Tools** | State + execution | Runtime libraries |
|
|
38
|
+
| **Apps** | Interactive views | UI framework |
|
|
39
|
+
| **Skills** | Platform adaptation | Compiler flags |
|
|
40
|
+
| **Compiler** | The LLM | gcc, clang, rustc |
|
|
41
|
+
|
|
42
|
+
Layers are wired together through **contracts** — typed `provides`/`consumes` declarations backed by JSON Schema. A prompt layer provides an output shape. A tool layer provides data schemas. An app layer consumes both.
|
|
43
|
+
|
|
44
|
+
## Quick Start
|
|
45
|
+
|
|
46
|
+
```
|
|
47
|
+
my-workflow/
|
|
48
|
+
├── mcp-contract.json # Manifest
|
|
49
|
+
├── prompts/ # Markdown reasoning
|
|
50
|
+
├── tools/ # MCP server
|
|
51
|
+
├── apps/ # Interactive UIs
|
|
52
|
+
├── schemas/ # JSON Schema contracts
|
|
53
|
+
└── skills/ # Platform hints
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
See [SPEC.md](SPEC.md) for the full specification.
|
|
57
|
+
|
|
58
|
+
## Reference Implementation
|
|
59
|
+
|
|
60
|
+
[Synth Ops](https://github.com/jmfullerton96/synthops) — intelligent infrastructure operations built as an MCP Contract bundle.
|
|
61
|
+
|
|
62
|
+
## Manifest Schema
|
|
63
|
+
|
|
64
|
+
The JSON Schema for validating `mcp-contract.json` manifests is at [`schema/mcp-contract.schema.json`](schema/mcp-contract.schema.json).
|
|
65
|
+
|
|
66
|
+
Reference it in your manifest:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"$schema": "https://mcpcontracts.com/schema/0.1.0.json",
|
|
71
|
+
"name": "my-workflow",
|
|
72
|
+
"version": "0.1.0",
|
|
73
|
+
"layers": { ... }
|
|
74
|
+
}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Status
|
|
78
|
+
|
|
79
|
+
**v0.1.0-draft** — Spec, manifest schema, CLI (`mcpc validate`, `init`, `pack`, `test`), and one reference implementation.
|
|
80
|
+
|
|
81
|
+
## License
|
|
82
|
+
|
|
83
|
+
Apache-2.0
|
mcpc_cli-0.1.0/README.md
ADDED
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# MCP Contract
|
|
2
|
+
|
|
3
|
+
[](https://github.com/jmfullerton96/mcp-contract/actions/workflows/ci.yml)
|
|
4
|
+
[](SPEC.md)
|
|
5
|
+
[](LICENSE)
|
|
6
|
+
[](schema/mcp-contract.schema.json)
|
|
7
|
+
[](https://mcpcontracts.com)
|
|
8
|
+
|
|
9
|
+
A specification for composable AI workflow bundles with typed contracts between layers.
|
|
10
|
+
|
|
11
|
+
## The Problem
|
|
12
|
+
|
|
13
|
+
MCP servers are monoliths. If you want to use one server's reasoning prompts with another server's tools and a third server's UI, you fork all three. MCP Contract fixes this by separating bundles into independently authored, versioned, and composable layers.
|
|
14
|
+
|
|
15
|
+
## The Layers
|
|
16
|
+
|
|
17
|
+
| Layer | Role | Analogy |
|
|
18
|
+
|---|---|---|
|
|
19
|
+
| **Prompts** | Reasoning logic | Source code |
|
|
20
|
+
| **Tools** | State + execution | Runtime libraries |
|
|
21
|
+
| **Apps** | Interactive views | UI framework |
|
|
22
|
+
| **Skills** | Platform adaptation | Compiler flags |
|
|
23
|
+
| **Compiler** | The LLM | gcc, clang, rustc |
|
|
24
|
+
|
|
25
|
+
Layers are wired together through **contracts** — typed `provides`/`consumes` declarations backed by JSON Schema. A prompt layer provides an output shape. A tool layer provides data schemas. An app layer consumes both.
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
```
|
|
30
|
+
my-workflow/
|
|
31
|
+
├── mcp-contract.json # Manifest
|
|
32
|
+
├── prompts/ # Markdown reasoning
|
|
33
|
+
├── tools/ # MCP server
|
|
34
|
+
├── apps/ # Interactive UIs
|
|
35
|
+
├── schemas/ # JSON Schema contracts
|
|
36
|
+
└── skills/ # Platform hints
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
See [SPEC.md](SPEC.md) for the full specification.
|
|
40
|
+
|
|
41
|
+
## Reference Implementation
|
|
42
|
+
|
|
43
|
+
[Synth Ops](https://github.com/jmfullerton96/synthops) — intelligent infrastructure operations built as an MCP Contract bundle.
|
|
44
|
+
|
|
45
|
+
## Manifest Schema
|
|
46
|
+
|
|
47
|
+
The JSON Schema for validating `mcp-contract.json` manifests is at [`schema/mcp-contract.schema.json`](schema/mcp-contract.schema.json).
|
|
48
|
+
|
|
49
|
+
Reference it in your manifest:
|
|
50
|
+
|
|
51
|
+
```json
|
|
52
|
+
{
|
|
53
|
+
"$schema": "https://mcpcontracts.com/schema/0.1.0.json",
|
|
54
|
+
"name": "my-workflow",
|
|
55
|
+
"version": "0.1.0",
|
|
56
|
+
"layers": { ... }
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Status
|
|
61
|
+
|
|
62
|
+
**v0.1.0-draft** — Spec, manifest schema, CLI (`mcpc validate`, `init`, `pack`, `test`), and one reference implementation.
|
|
63
|
+
|
|
64
|
+
## License
|
|
65
|
+
|
|
66
|
+
Apache-2.0
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"""mcpc CLI entry point."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import argparse
|
|
6
|
+
import sys
|
|
7
|
+
|
|
8
|
+
from mcpc import __version__
|
|
9
|
+
from mcpc.init import TEMPLATES, init_bundle
|
|
10
|
+
from mcpc.pack import pack_bundle
|
|
11
|
+
from mcpc.test import test_bundle
|
|
12
|
+
from mcpc.unpack import unpack_bundle
|
|
13
|
+
from mcpc.validate import validate_bundle
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
def main(argv: list[str] | None = None) -> None:
|
|
17
|
+
parser = argparse.ArgumentParser(
|
|
18
|
+
prog="mcpc",
|
|
19
|
+
description="CLI for the MCP Contract specification.",
|
|
20
|
+
)
|
|
21
|
+
parser.add_argument(
|
|
22
|
+
"--version", action="version", version=f"mcpc {__version__}"
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
subparsers = parser.add_subparsers(dest="command")
|
|
26
|
+
|
|
27
|
+
# -- validate --
|
|
28
|
+
validate_parser = subparsers.add_parser(
|
|
29
|
+
"validate",
|
|
30
|
+
help="Validate an MCP Contract bundle.",
|
|
31
|
+
description=(
|
|
32
|
+
"Validates the manifest against the schema, checks that all "
|
|
33
|
+
"provides/consumes contracts are satisfied, and verifies that "
|
|
34
|
+
"referenced files exist."
|
|
35
|
+
),
|
|
36
|
+
)
|
|
37
|
+
validate_parser.add_argument(
|
|
38
|
+
"path",
|
|
39
|
+
nargs="?",
|
|
40
|
+
default=".",
|
|
41
|
+
help="Path to the bundle directory (default: current directory).",
|
|
42
|
+
)
|
|
43
|
+
validate_parser.add_argument(
|
|
44
|
+
"--quiet",
|
|
45
|
+
action="store_true",
|
|
46
|
+
help="Only print errors, suppress informational output.",
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
# -- init --
|
|
50
|
+
init_parser = subparsers.add_parser(
|
|
51
|
+
"init",
|
|
52
|
+
help="Scaffold a new MCP Contract bundle.",
|
|
53
|
+
description="Creates a new bundle directory with manifest and starter files.",
|
|
54
|
+
)
|
|
55
|
+
init_parser.add_argument(
|
|
56
|
+
"name",
|
|
57
|
+
help="Bundle name (lowercase, hyphens only).",
|
|
58
|
+
)
|
|
59
|
+
init_parser.add_argument(
|
|
60
|
+
"--path",
|
|
61
|
+
default=".",
|
|
62
|
+
help="Parent directory for the new bundle (default: current directory).",
|
|
63
|
+
)
|
|
64
|
+
init_parser.add_argument(
|
|
65
|
+
"--template",
|
|
66
|
+
choices=list(TEMPLATES.keys()),
|
|
67
|
+
default="full",
|
|
68
|
+
help="Bundle template (default: full).",
|
|
69
|
+
)
|
|
70
|
+
|
|
71
|
+
# -- pack --
|
|
72
|
+
pack_parser = subparsers.add_parser(
|
|
73
|
+
"pack",
|
|
74
|
+
help="Pack a bundle into a .mcpc archive.",
|
|
75
|
+
description=(
|
|
76
|
+
"Validates the bundle, then creates a .mcpc archive (zip) "
|
|
77
|
+
"containing all bundle files."
|
|
78
|
+
),
|
|
79
|
+
)
|
|
80
|
+
pack_parser.add_argument(
|
|
81
|
+
"path",
|
|
82
|
+
nargs="?",
|
|
83
|
+
default=".",
|
|
84
|
+
help="Path to the bundle directory (default: current directory).",
|
|
85
|
+
)
|
|
86
|
+
pack_parser.add_argument(
|
|
87
|
+
"--output", "-o",
|
|
88
|
+
default=None,
|
|
89
|
+
help="Output file path (default: <name>-<version>.mcpc in parent directory).",
|
|
90
|
+
)
|
|
91
|
+
pack_parser.add_argument(
|
|
92
|
+
"--quiet",
|
|
93
|
+
action="store_true",
|
|
94
|
+
help="Only print errors, suppress informational output.",
|
|
95
|
+
)
|
|
96
|
+
|
|
97
|
+
# -- test --
|
|
98
|
+
test_parser = subparsers.add_parser(
|
|
99
|
+
"test",
|
|
100
|
+
help="Run structural tests on bundle layers.",
|
|
101
|
+
description=(
|
|
102
|
+
"Tests layer content quality: prompt frontmatter, schema "
|
|
103
|
+
"conventions, tool syntax, and app structure."
|
|
104
|
+
),
|
|
105
|
+
)
|
|
106
|
+
test_parser.add_argument(
|
|
107
|
+
"path",
|
|
108
|
+
nargs="?",
|
|
109
|
+
default=".",
|
|
110
|
+
help="Path to the bundle directory (default: current directory).",
|
|
111
|
+
)
|
|
112
|
+
test_parser.add_argument(
|
|
113
|
+
"--layer",
|
|
114
|
+
choices=["prompts", "schemas", "tools", "apps"],
|
|
115
|
+
default=None,
|
|
116
|
+
help="Test only a specific layer.",
|
|
117
|
+
)
|
|
118
|
+
test_parser.add_argument(
|
|
119
|
+
"--quiet",
|
|
120
|
+
action="store_true",
|
|
121
|
+
help="Only print failures, suppress informational output.",
|
|
122
|
+
)
|
|
123
|
+
|
|
124
|
+
# -- unpack --
|
|
125
|
+
unpack_parser = subparsers.add_parser(
|
|
126
|
+
"unpack",
|
|
127
|
+
help="Extract a .mcpc archive into a bundle directory.",
|
|
128
|
+
description=(
|
|
129
|
+
"Extracts a .mcpc archive, verifies it contains a valid "
|
|
130
|
+
"manifest, and writes the bundle to a directory."
|
|
131
|
+
),
|
|
132
|
+
)
|
|
133
|
+
unpack_parser.add_argument(
|
|
134
|
+
"archive",
|
|
135
|
+
help="Path to the .mcpc archive.",
|
|
136
|
+
)
|
|
137
|
+
unpack_parser.add_argument(
|
|
138
|
+
"--output", "-o",
|
|
139
|
+
default=None,
|
|
140
|
+
help="Output directory (default: archive stem in current directory).",
|
|
141
|
+
)
|
|
142
|
+
unpack_parser.add_argument(
|
|
143
|
+
"--quiet",
|
|
144
|
+
action="store_true",
|
|
145
|
+
help="Only print errors, suppress informational output.",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
args = parser.parse_args(argv)
|
|
149
|
+
|
|
150
|
+
if args.command is None:
|
|
151
|
+
parser.print_help()
|
|
152
|
+
sys.exit(0)
|
|
153
|
+
|
|
154
|
+
if args.command == "validate":
|
|
155
|
+
ok = validate_bundle(args.path, quiet=args.quiet)
|
|
156
|
+
sys.exit(0 if ok else 1)
|
|
157
|
+
|
|
158
|
+
if args.command == "init":
|
|
159
|
+
ok = init_bundle(args.name, args.path, args.template)
|
|
160
|
+
sys.exit(0 if ok else 1)
|
|
161
|
+
|
|
162
|
+
if args.command == "pack":
|
|
163
|
+
ok = pack_bundle(args.path, output=args.output, quiet=args.quiet)
|
|
164
|
+
sys.exit(0 if ok else 1)
|
|
165
|
+
|
|
166
|
+
if args.command == "test":
|
|
167
|
+
ok = test_bundle(args.path, layer=args.layer, quiet=args.quiet)
|
|
168
|
+
sys.exit(0 if ok else 1)
|
|
169
|
+
|
|
170
|
+
if args.command == "unpack":
|
|
171
|
+
ok = unpack_bundle(args.archive, output=args.output, quiet=args.quiet)
|
|
172
|
+
sys.exit(0 if ok else 1)
|
|
@@ -0,0 +1,300 @@
|
|
|
1
|
+
"""Scaffold a new MCP Contract bundle."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import json
|
|
6
|
+
import os
|
|
7
|
+
import sys
|
|
8
|
+
from pathlib import Path
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
_BOLD = "\033[1m"
|
|
12
|
+
_GREEN = "\033[32m"
|
|
13
|
+
_CYAN = "\033[36m"
|
|
14
|
+
_RESET = "\033[0m"
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def _supports_color() -> bool:
|
|
18
|
+
return hasattr(sys.stdout, "isatty") and sys.stdout.isatty()
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
def _fmt(code: str, text: str) -> str:
|
|
22
|
+
return f"{code}{text}{_RESET}" if _supports_color() else text
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
# ---------------------------------------------------------------------------
|
|
26
|
+
# Templates
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
|
|
29
|
+
TEMPLATES = {
|
|
30
|
+
"full": {
|
|
31
|
+
"description": "Complete bundle with all layers (prompts, tools, apps, skills)",
|
|
32
|
+
"layers": ["prompts", "tools", "apps", "skills", "schemas"],
|
|
33
|
+
},
|
|
34
|
+
"prompts-only": {
|
|
35
|
+
"description": "Minimal bundle with prompts layer only — no tools, apps, or skills",
|
|
36
|
+
"layers": ["prompts", "schemas"],
|
|
37
|
+
},
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def _make_manifest(name: str, template: str) -> dict:
|
|
42
|
+
manifest: dict = {
|
|
43
|
+
"$schema": "https://mcpcontracts.com/schema/0.1.0.json",
|
|
44
|
+
"name": name,
|
|
45
|
+
"version": "0.1.0",
|
|
46
|
+
"description": "",
|
|
47
|
+
"author": {"name": ""},
|
|
48
|
+
"license": "Apache-2.0",
|
|
49
|
+
"layers": {},
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
layers = manifest["layers"]
|
|
53
|
+
|
|
54
|
+
if template in ("full", "prompts-only"):
|
|
55
|
+
layers["prompts"] = {
|
|
56
|
+
"entry": "prompts/main.md",
|
|
57
|
+
"provides": [
|
|
58
|
+
{
|
|
59
|
+
"name": f"{name}-output",
|
|
60
|
+
"schema": f"schemas/{name}-output.json",
|
|
61
|
+
"description": f"Output shape for {name} workflow.",
|
|
62
|
+
}
|
|
63
|
+
],
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
if template == "full":
|
|
67
|
+
layers["tools"] = {
|
|
68
|
+
"entry": "tools/server.py",
|
|
69
|
+
"runtime": "python",
|
|
70
|
+
"provides": [
|
|
71
|
+
{
|
|
72
|
+
"name": f"{name}-data",
|
|
73
|
+
"schema": f"schemas/{name}-data.json",
|
|
74
|
+
"description": f"Data provided by {name} tool server.",
|
|
75
|
+
}
|
|
76
|
+
],
|
|
77
|
+
}
|
|
78
|
+
layers["apps"] = {
|
|
79
|
+
"entry": "apps/main.html",
|
|
80
|
+
"consumes": [
|
|
81
|
+
{
|
|
82
|
+
"name": f"{name}-data",
|
|
83
|
+
"schema": f"schemas/{name}-data.json",
|
|
84
|
+
},
|
|
85
|
+
{
|
|
86
|
+
"name": f"{name}-output",
|
|
87
|
+
"schema": f"schemas/{name}-output.json",
|
|
88
|
+
},
|
|
89
|
+
],
|
|
90
|
+
}
|
|
91
|
+
layers["skills"] = {
|
|
92
|
+
"targets": {
|
|
93
|
+
"claude": "skills/claude.md",
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
manifest["compiler_compatibility"] = ["claude"]
|
|
97
|
+
manifest["compose"] = {
|
|
98
|
+
"chain": ["prompts", "tools", "apps"],
|
|
99
|
+
"fallback": "prompts-only",
|
|
100
|
+
}
|
|
101
|
+
else:
|
|
102
|
+
manifest["compiler_compatibility"] = ["claude", "gpt", "gemini"]
|
|
103
|
+
manifest["compose"] = {
|
|
104
|
+
"chain": ["prompts"],
|
|
105
|
+
"fallback": "prompts-only",
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
return manifest
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
def _make_schema(name: str, title: str, description: str) -> dict:
|
|
112
|
+
return {
|
|
113
|
+
"$id": f"https://mcpcontracts.com/schemas/{name}/0.1.0",
|
|
114
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
115
|
+
"title": title,
|
|
116
|
+
"description": description,
|
|
117
|
+
"type": "object",
|
|
118
|
+
"required": [],
|
|
119
|
+
"properties": {},
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
def _make_prompt(bundle_name: str, provides_name: str) -> str:
|
|
124
|
+
return f"""---
|
|
125
|
+
name: {bundle_name}
|
|
126
|
+
version: 0.1.0
|
|
127
|
+
description: Primary methodology for {bundle_name}.
|
|
128
|
+
provides: {provides_name}
|
|
129
|
+
---
|
|
130
|
+
|
|
131
|
+
# {bundle_name.replace('-', ' ').title()}
|
|
132
|
+
|
|
133
|
+
Describe your workflow's reasoning methodology here.
|
|
134
|
+
|
|
135
|
+
## Methodology
|
|
136
|
+
|
|
137
|
+
1. **Step one** — What to analyze first.
|
|
138
|
+
2. **Step two** — How to structure the analysis.
|
|
139
|
+
3. **Step three** — What output to produce.
|
|
140
|
+
|
|
141
|
+
## Output Shape
|
|
142
|
+
|
|
143
|
+
The analysis produces structured output conforming to the
|
|
144
|
+
`{provides_name}` schema.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
|
|
148
|
+
def _make_tool_stub(bundle_name: str) -> str:
|
|
149
|
+
return f'''"""
|
|
150
|
+
{bundle_name} MCP Tool Server (Stub)
|
|
151
|
+
|
|
152
|
+
Implement your MCP tools here. Each tool should:
|
|
153
|
+
- Declare typed input/output schemas in schemas/
|
|
154
|
+
- Not embed reasoning logic (that belongs in prompts/)
|
|
155
|
+
- Not assume a specific prompt or app layer
|
|
156
|
+
"""
|
|
157
|
+
'''
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
def _make_app_stub(bundle_name: str) -> str:
|
|
161
|
+
return f"""<!--
|
|
162
|
+
{bundle_name} App (Stub)
|
|
163
|
+
|
|
164
|
+
Implement your interactive view here. The app should:
|
|
165
|
+
- Declare every schema it consumes in the manifest
|
|
166
|
+
- Not call external APIs directly (data flows through tools)
|
|
167
|
+
- Be renderable with mock data for independent testing
|
|
168
|
+
-->
|
|
169
|
+
<!DOCTYPE html>
|
|
170
|
+
<html lang="en">
|
|
171
|
+
<head>
|
|
172
|
+
<meta charset="UTF-8">
|
|
173
|
+
<title>{bundle_name.replace('-', ' ').title()}</title>
|
|
174
|
+
</head>
|
|
175
|
+
<body>
|
|
176
|
+
<h1>{bundle_name.replace('-', ' ').title()}</h1>
|
|
177
|
+
</body>
|
|
178
|
+
</html>
|
|
179
|
+
"""
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
def _make_skill(bundle_name: str) -> str:
|
|
183
|
+
return f"""---
|
|
184
|
+
target: claude
|
|
185
|
+
version: 0.1.0
|
|
186
|
+
---
|
|
187
|
+
|
|
188
|
+
# Claude Platform Skill
|
|
189
|
+
|
|
190
|
+
Platform-specific adaptation for running {bundle_name} on Claude.
|
|
191
|
+
|
|
192
|
+
## Tool Registration
|
|
193
|
+
|
|
194
|
+
Register the tool server via MCP stdio transport in Claude Desktop config.
|
|
195
|
+
|
|
196
|
+
## Context Window
|
|
197
|
+
|
|
198
|
+
Estimate your prompt chain token count here. Claude Opus supports 200k context.
|
|
199
|
+
|
|
200
|
+
## Graceful Degradation
|
|
201
|
+
|
|
202
|
+
If the tool server is unavailable, the prompt layer should still produce
|
|
203
|
+
useful output from the LLM's training data.
|
|
204
|
+
"""
|
|
205
|
+
|
|
206
|
+
|
|
207
|
+
# ---------------------------------------------------------------------------
|
|
208
|
+
# Public API
|
|
209
|
+
# ---------------------------------------------------------------------------
|
|
210
|
+
|
|
211
|
+
def init_bundle(name: str, path: str, template: str) -> bool:
|
|
212
|
+
"""Scaffold a new MCP Contract bundle. Returns True on success."""
|
|
213
|
+
bundle_dir = Path(path).resolve() / name
|
|
214
|
+
|
|
215
|
+
if bundle_dir.exists():
|
|
216
|
+
print(f" error: {bundle_dir} already exists", file=sys.stderr)
|
|
217
|
+
return False
|
|
218
|
+
|
|
219
|
+
tmpl = TEMPLATES[template]
|
|
220
|
+
manifest = _make_manifest(name, template)
|
|
221
|
+
|
|
222
|
+
print(f"\n{_fmt(_BOLD, 'mcpc init')} {name} ({tmpl['description']})\n")
|
|
223
|
+
|
|
224
|
+
# Create directories
|
|
225
|
+
dirs = [bundle_dir / layer for layer in tmpl["layers"]]
|
|
226
|
+
for d in dirs:
|
|
227
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
228
|
+
|
|
229
|
+
created: list[str] = []
|
|
230
|
+
|
|
231
|
+
# Write manifest
|
|
232
|
+
manifest_path = bundle_dir / "mcp-contract.json"
|
|
233
|
+
with open(manifest_path, "w") as f:
|
|
234
|
+
json.dump(manifest, f, indent=2)
|
|
235
|
+
f.write("\n")
|
|
236
|
+
created.append("mcp-contract.json")
|
|
237
|
+
|
|
238
|
+
# Write prompt
|
|
239
|
+
prompt_path = bundle_dir / "prompts" / "main.md"
|
|
240
|
+
with open(prompt_path, "w") as f:
|
|
241
|
+
f.write(_make_prompt(name, f"{name}-output"))
|
|
242
|
+
created.append("prompts/main.md")
|
|
243
|
+
|
|
244
|
+
# Write output schema
|
|
245
|
+
schema_path = bundle_dir / "schemas" / f"{name}-output.json"
|
|
246
|
+
with open(schema_path, "w") as f:
|
|
247
|
+
json.dump(
|
|
248
|
+
_make_schema(
|
|
249
|
+
f"{name}-output",
|
|
250
|
+
f"{name.replace('-', ' ').title()} Output",
|
|
251
|
+
f"Output shape for {name} workflow.",
|
|
252
|
+
),
|
|
253
|
+
f,
|
|
254
|
+
indent=2,
|
|
255
|
+
)
|
|
256
|
+
f.write("\n")
|
|
257
|
+
created.append(f"schemas/{name}-output.json")
|
|
258
|
+
|
|
259
|
+
if template == "full":
|
|
260
|
+
# Data schema
|
|
261
|
+
data_schema_path = bundle_dir / "schemas" / f"{name}-data.json"
|
|
262
|
+
with open(data_schema_path, "w") as f:
|
|
263
|
+
json.dump(
|
|
264
|
+
_make_schema(
|
|
265
|
+
f"{name}-data",
|
|
266
|
+
f"{name.replace('-', ' ').title()} Data",
|
|
267
|
+
f"Data provided by {name} tool server.",
|
|
268
|
+
),
|
|
269
|
+
f,
|
|
270
|
+
indent=2,
|
|
271
|
+
)
|
|
272
|
+
f.write("\n")
|
|
273
|
+
created.append(f"schemas/{name}-data.json")
|
|
274
|
+
|
|
275
|
+
# Tool stub
|
|
276
|
+
tool_path = bundle_dir / "tools" / "server.py"
|
|
277
|
+
with open(tool_path, "w") as f:
|
|
278
|
+
f.write(_make_tool_stub(name))
|
|
279
|
+
created.append("tools/server.py")
|
|
280
|
+
|
|
281
|
+
# App stub
|
|
282
|
+
app_path = bundle_dir / "apps" / "main.html"
|
|
283
|
+
with open(app_path, "w") as f:
|
|
284
|
+
f.write(_make_app_stub(name))
|
|
285
|
+
created.append("apps/main.html")
|
|
286
|
+
|
|
287
|
+
# Skill
|
|
288
|
+
skill_path = bundle_dir / "skills" / "claude.md"
|
|
289
|
+
with open(skill_path, "w") as f:
|
|
290
|
+
f.write(_make_skill(name))
|
|
291
|
+
created.append("skills/claude.md")
|
|
292
|
+
|
|
293
|
+
# Report
|
|
294
|
+
for f in created:
|
|
295
|
+
print(f" {_fmt(_GREEN, '+')} {f}")
|
|
296
|
+
|
|
297
|
+
print(f"\n{_fmt(_CYAN, ' info:')} created {len(created)} files in {bundle_dir}")
|
|
298
|
+
print(f"{_fmt(_CYAN, ' info:')} run {_fmt(_BOLD, f'mcpc validate {name}')} to check\n")
|
|
299
|
+
|
|
300
|
+
return True
|