@christopher_dondici/mcp-gen 2.1.2
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.
- package/CHANGELOG.md +217 -0
- package/LICENSE +21 -0
- package/README.md +439 -0
- package/README.pt-BR.md +320 -0
- package/RELEASE_NOTES.md +98 -0
- package/SECURITY.md +75 -0
- package/SECURITY.pt-BR.md +77 -0
- package/dist/cli/index.d.ts +3 -0
- package/dist/cli/index.d.ts.map +1 -0
- package/dist/cli/index.js +685 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/core/generator.d.ts +4 -0
- package/dist/core/generator.d.ts.map +1 -0
- package/dist/core/generator.js +275 -0
- package/dist/core/generator.js.map +1 -0
- package/dist/core/incremental.d.ts +25 -0
- package/dist/core/incremental.d.ts.map +1 -0
- package/dist/core/incremental.js +91 -0
- package/dist/core/incremental.js.map +1 -0
- package/dist/core/parser.d.ts +3 -0
- package/dist/core/parser.d.ts.map +1 -0
- package/dist/core/parser.js +372 -0
- package/dist/core/parser.js.map +1 -0
- package/dist/core/registry.d.ts +13 -0
- package/dist/core/registry.d.ts.map +1 -0
- package/dist/core/registry.js +107 -0
- package/dist/core/registry.js.map +1 -0
- package/dist/core/security-lint.d.ts +53 -0
- package/dist/core/security-lint.d.ts.map +1 -0
- package/dist/core/security-lint.js +470 -0
- package/dist/core/security-lint.js.map +1 -0
- package/dist/core/security.d.ts +41 -0
- package/dist/core/security.d.ts.map +1 -0
- package/dist/core/security.js +150 -0
- package/dist/core/security.js.map +1 -0
- package/dist/core/templating.d.ts +5 -0
- package/dist/core/templating.d.ts.map +1 -0
- package/dist/core/templating.js +211 -0
- package/dist/core/templating.js.map +1 -0
- package/dist/core/types.d.ts +104 -0
- package/dist/core/types.d.ts.map +1 -0
- package/dist/core/types.js +3 -0
- package/dist/core/types.js.map +1 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +20 -0
- package/dist/index.js.map +1 -0
- package/dist/templates/go/Dockerfile.hbs +16 -0
- package/dist/templates/go/README.md.hbs +77 -0
- package/dist/templates/go/ci.yml.hbs +28 -0
- package/dist/templates/go/client.go.hbs +86 -0
- package/dist/templates/go/go.mod.hbs +5 -0
- package/dist/templates/go/models.go.hbs +28 -0
- package/dist/templates/go/server.go.hbs +220 -0
- package/dist/templates/python/Dockerfile.hbs +12 -0
- package/dist/templates/python/README.md.hbs +115 -0
- package/dist/templates/python/ci.yml.hbs +24 -0
- package/dist/templates/python/models.py.hbs +20 -0
- package/dist/templates/python/requirements.txt.hbs +3 -0
- package/dist/templates/python/server.py.hbs +195 -0
- package/dist/templates/typescript/Dockerfile.hbs +18 -0
- package/dist/templates/typescript/README.md.hbs +91 -0
- package/dist/templates/typescript/ci.yml.hbs +28 -0
- package/dist/templates/typescript/client.hbs +49 -0
- package/dist/templates/typescript/models.hbs +23 -0
- package/dist/templates/typescript/package.json.hbs +26 -0
- package/dist/templates/typescript/server.hbs +337 -0
- package/dist/templates/typescript/tsconfig.json.hbs +17 -0
- package/examples/petstore.json +130 -0
- package/examples/petstore.yaml +131 -0
- package/examples/week3.json +47 -0
- package/package.json +64 -0
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
build-and-test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Setup Go
|
|
16
|
+
uses: actions/setup-go@v5
|
|
17
|
+
with:
|
|
18
|
+
go-version: "1.22"
|
|
19
|
+
cache: true
|
|
20
|
+
|
|
21
|
+
- name: Download dependencies
|
|
22
|
+
run: go mod download
|
|
23
|
+
|
|
24
|
+
- name: Build
|
|
25
|
+
run: go build -v .
|
|
26
|
+
|
|
27
|
+
- name: Test
|
|
28
|
+
run: go test -v ./...
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
// Auto-generated client SDK for {{info.title}} v{{info.version}}
|
|
2
|
+
// Generated: {{generatedAt}}
|
|
3
|
+
|
|
4
|
+
package main
|
|
5
|
+
|
|
6
|
+
import (
|
|
7
|
+
"bytes"
|
|
8
|
+
"encoding/json"
|
|
9
|
+
"fmt"
|
|
10
|
+
"io"
|
|
11
|
+
"net/http"
|
|
12
|
+
"strings"
|
|
13
|
+
"time"
|
|
14
|
+
)
|
|
15
|
+
|
|
16
|
+
type APIClient struct {
|
|
17
|
+
BaseURL string
|
|
18
|
+
Token string
|
|
19
|
+
HTTP *http.Client
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
func NewAPIClient(baseURL, token string) *APIClient {
|
|
23
|
+
if token == "" {
|
|
24
|
+
token = apiToken
|
|
25
|
+
}
|
|
26
|
+
return &APIClient{BaseURL: strings.TrimRight(baseURL, "/"), Token: token, HTTP: &http.Client{Timeout: 30 * time.Second}}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
type APIError struct {
|
|
30
|
+
Status int
|
|
31
|
+
Body string
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
func (e *APIError) Error() string {
|
|
35
|
+
return fmt.Sprintf("HTTP %d: %s", e.Status, e.Body)
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
func (c *APIClient) do(method, path string, params map[string]any) (any, error) {
|
|
39
|
+
path = c.expand(path, params)
|
|
40
|
+
var body io.Reader
|
|
41
|
+
if v, ok := params["body"]; ok {
|
|
42
|
+
b, err := json.Marshal(v)
|
|
43
|
+
if err != nil {
|
|
44
|
+
return nil, err
|
|
45
|
+
}
|
|
46
|
+
body = bytes.NewReader(b)
|
|
47
|
+
}
|
|
48
|
+
req, err := http.NewRequest(method, c.BaseURL+path, body)
|
|
49
|
+
if err != nil {
|
|
50
|
+
return nil, err
|
|
51
|
+
}
|
|
52
|
+
if c.Token != "" {
|
|
53
|
+
req.Header.Set("Authorization", "Bearer "+c.Token)
|
|
54
|
+
}
|
|
55
|
+
if body != nil {
|
|
56
|
+
req.Header.Set("Content-Type", "application/json")
|
|
57
|
+
}
|
|
58
|
+
{{#each (allHeaderParams tools)}}
|
|
59
|
+
if v, ok := params["{{name}}"]; ok {
|
|
60
|
+
req.Header.Set("{{name}}", fmt.Sprintf("%v", v))
|
|
61
|
+
}
|
|
62
|
+
{{/each}}
|
|
63
|
+
resp, err := c.HTTP.Do(req)
|
|
64
|
+
if err != nil {
|
|
65
|
+
return nil, err
|
|
66
|
+
}
|
|
67
|
+
defer resp.Body.Close()
|
|
68
|
+
raw, _ := io.ReadAll(resp.Body)
|
|
69
|
+
if resp.StatusCode >= 400 {
|
|
70
|
+
return nil, &APIError{Status: resp.StatusCode, Body: string(raw)}
|
|
71
|
+
}
|
|
72
|
+
var out any
|
|
73
|
+
if len(raw) > 0 {
|
|
74
|
+
if err := json.Unmarshal(raw, &out); err != nil {
|
|
75
|
+
return string(raw), nil
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
return out, nil
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
func (c *APIClient) expand(path string, params map[string]any) string {
|
|
82
|
+
for k, v := range params {
|
|
83
|
+
path = strings.ReplaceAll(path, "{"+k+"}", fmt.Sprintf("%v", v))
|
|
84
|
+
}
|
|
85
|
+
return path
|
|
86
|
+
}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
// Auto-generated by mcp-gen v{{generatorVersion}} — do not edit.
|
|
2
|
+
// Spec: {{info.title}} v{{info.version}}
|
|
3
|
+
|
|
4
|
+
package main
|
|
5
|
+
|
|
6
|
+
{{#each models}}
|
|
7
|
+
{{#if isEnum}}
|
|
8
|
+
// {{escapeText description}}
|
|
9
|
+
type {{name}} = {{#each enumValues}}{{#if (eq (typeof this) "string")}}"{{escapeLiteral this}}"{{else}}{{this}}{{/if}}{{#unless @last}} | {{/unless}}{{/each}}
|
|
10
|
+
|
|
11
|
+
{{else if oneOf}}
|
|
12
|
+
// {{escapeText description}}
|
|
13
|
+
type {{name}} = {{#each oneOf}}{{this}}{{#unless @last}} | {{/unless}}{{/each}}
|
|
14
|
+
|
|
15
|
+
{{else if anyOf}}
|
|
16
|
+
// {{escapeText description}}
|
|
17
|
+
type {{name}} = {{#each anyOf}}{{this}}{{#unless @last}} | {{/unless}}{{/each}}
|
|
18
|
+
|
|
19
|
+
{{else}}
|
|
20
|
+
// {{escapeText description}}
|
|
21
|
+
type {{name}} struct {
|
|
22
|
+
{{#each properties}}
|
|
23
|
+
{{pascal name}} {{#if ref}}{{ref}}{{#if isArray}}[]{{/if}}{{else if isArray}}[]any{{else}}{{goType type}}{{/if}} `json:"{{name}}{{#unless (includes ../required name)}},omitempty{{/unless}}"`{{#if description}} // {{escapeText description}}{{/if}}
|
|
24
|
+
{{/each}}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
{{/if}}
|
|
28
|
+
{{/each}}
|
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
// Auto-generated by mcp-gen v{{generatorVersion}} — do not edit.
|
|
2
|
+
// Spec: {{info.title}} v{{info.version}}
|
|
3
|
+
// Generated: {{generatedAt}}
|
|
4
|
+
{{#if incremental}}
|
|
5
|
+
// Incremental mode: edit code between @@mcp-gen markers — it will be preserved on re-generation.
|
|
6
|
+
{{/if}}
|
|
7
|
+
package main
|
|
8
|
+
|
|
9
|
+
import (
|
|
10
|
+
"context"
|
|
11
|
+
"encoding/json"
|
|
12
|
+
"fmt"
|
|
13
|
+
"os"
|
|
14
|
+
"strings"
|
|
15
|
+
|
|
16
|
+
"github.com/mark3labs/mcp-go/mcp"
|
|
17
|
+
"github.com/mark3labs/mcp-go/server"
|
|
18
|
+
)
|
|
19
|
+
|
|
20
|
+
var baseURL = "{{baseUrl}}"
|
|
21
|
+
var apiToken = os.Getenv("TOKEN")
|
|
22
|
+
|
|
23
|
+
// RAW_CREDENTIAL_KEYS defines credential keywords that are blocked by default
|
|
24
|
+
var RAW_CREDENTIAL_KEYS = map[string]bool{
|
|
25
|
+
"authorization": true,
|
|
26
|
+
"token": true,
|
|
27
|
+
"access_token": true,
|
|
28
|
+
"api_key": true,
|
|
29
|
+
"apikey": true,
|
|
30
|
+
"x_api_key": true,
|
|
31
|
+
"client_secret": true,
|
|
32
|
+
"refresh_token": true,
|
|
33
|
+
"password": true,
|
|
34
|
+
"secret": true,
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// TOOL_POLICIES defines per-tool security policies
|
|
38
|
+
var TOOL_POLICIES = map[string]struct {
|
|
39
|
+
Endpoint string
|
|
40
|
+
RequiresTTL bool
|
|
41
|
+
RequiresSpendLimit bool
|
|
42
|
+
RequiresRequestLog bool
|
|
43
|
+
RequiresRevocationCheck bool
|
|
44
|
+
}{
|
|
45
|
+
{{#each tools}}
|
|
46
|
+
"{{name}}": {
|
|
47
|
+
Endpoint: "{{method}} {{path}}",
|
|
48
|
+
RequiresTTL: true,
|
|
49
|
+
RequiresSpendLimit: true,
|
|
50
|
+
RequiresRequestLog: true,
|
|
51
|
+
RequiresRevocationCheck: true,
|
|
52
|
+
},
|
|
53
|
+
{{/each}}
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
func jsonSerialize(v any) (*mcp.CallToolResult, error) {
|
|
57
|
+
b, err := json.MarshalIndent(v, "", " ")
|
|
58
|
+
if err != nil {
|
|
59
|
+
return nil, err
|
|
60
|
+
}
|
|
61
|
+
return mcp.NewToolResultText(string(b)), nil
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
func jsonExample(s string) any {
|
|
65
|
+
var v any
|
|
66
|
+
if err := json.Unmarshal([]byte(s), &v); err != nil {
|
|
67
|
+
return nil
|
|
68
|
+
}
|
|
69
|
+
return v
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
func hasRawCredentialKey(value any) bool {
|
|
73
|
+
if value == nil {
|
|
74
|
+
return false
|
|
75
|
+
}
|
|
76
|
+
switch v := value.(type) {
|
|
77
|
+
case map[string]any:
|
|
78
|
+
for key, nested := range v {
|
|
79
|
+
normalized := strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(key, "-", ""), "_", ""))
|
|
80
|
+
for candidate := range RAW_CREDENTIAL_KEYS {
|
|
81
|
+
candNorm := strings.ToLower(strings.ReplaceAll(strings.ReplaceAll(candidate, "-", ""), "_", ""))
|
|
82
|
+
if normalized == candNorm {
|
|
83
|
+
return true
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
if hasRawCredentialKey(nested) {
|
|
87
|
+
return true
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
case []any:
|
|
91
|
+
for _, item := range v {
|
|
92
|
+
if hasRawCredentialKey(item) {
|
|
93
|
+
return true
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return false
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
func requireSecurity(toolName string, security any, authContext map[string]any, requestArgs map[string]any) error {
|
|
101
|
+
if requestArgs != nil && hasRawCredentialKey(requestArgs) {
|
|
102
|
+
return fmt.Errorf("raw provider credentials are not allowed in tool arguments. use scoped auth_context metadata only")
|
|
103
|
+
}
|
|
104
|
+
if authContext != nil && hasRawCredentialKey(authContext) {
|
|
105
|
+
return fmt.Errorf("raw provider credentials are not allowed in auth_context. use scoped metadata only")
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
policy, ok := TOOL_POLICIES[toolName]
|
|
109
|
+
if !ok {
|
|
110
|
+
return fmt.Errorf("missing security policy for tool: %s", toolName)
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
if authContext == nil {
|
|
114
|
+
return fmt.Errorf("missing auth_context. provide scoped metadata (token_id, expires_at, limits, request_id) from your gateway")
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if policy.RequiresRevocationCheck {
|
|
118
|
+
if revoked, ok := authContext["revoked"].(bool); ok && revoked {
|
|
119
|
+
return fmt.Errorf("access revoked")
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
if policy.RequiresTTL {
|
|
124
|
+
if expiresAt, ok := authContext["expires_at"].(string); ok {
|
|
125
|
+
// Simple check - in production use proper parsing
|
|
126
|
+
if expiresAt == "" || expiresAt < "2025-01-01T00:00:00Z" {
|
|
127
|
+
return fmt.Errorf("token expired or missing TTL")
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
return fmt.Errorf("token expired or missing TTL")
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if policy.RequiresRequestLog {
|
|
135
|
+
if _, ok := authContext["request_id"].(string); !ok {
|
|
136
|
+
return fmt.Errorf("request_id is required for audit logging")
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
if policy.RequiresSpendLimit {
|
|
141
|
+
limit, hasLimit := authContext["spend_limit_usd"].(float64)
|
|
142
|
+
used, hasUsed := authContext["spend_used_usd"].(float64)
|
|
143
|
+
if !hasLimit || !hasUsed {
|
|
144
|
+
return fmt.Errorf("spend limit metadata is required")
|
|
145
|
+
}
|
|
146
|
+
if used > limit {
|
|
147
|
+
return fmt.Errorf("spend limit exceeded")
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if allowedTools, ok := authContext["allowed_tools"].([]any); ok {
|
|
152
|
+
found := false
|
|
153
|
+
for _, t := range allowedTools {
|
|
154
|
+
if ts, ok := t.(string); ok && ts == toolName {
|
|
155
|
+
found = true
|
|
156
|
+
break
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
if !found {
|
|
160
|
+
return fmt.Errorf("tool not allowed by scope: %s", toolName)
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if endpointAllowlist, ok := authContext["endpoint_allowlist"].([]any); ok {
|
|
165
|
+
found := false
|
|
166
|
+
for _, e := range endpointAllowlist {
|
|
167
|
+
if es, ok := e.(string); ok && es == policy.Endpoint {
|
|
168
|
+
found = true
|
|
169
|
+
break
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
if !found {
|
|
173
|
+
return fmt.Errorf("endpoint not allowed: %s", policy.Endpoint)
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
if security != nil {
|
|
178
|
+
if _, ok := authContext["token_id"].(string); !ok {
|
|
179
|
+
return fmt.Errorf("missing token_id for secured operation")
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
return nil
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
func main() {
|
|
187
|
+
s := server.NewMCPServer("{{serverName}}", "{{serverVersion}}")
|
|
188
|
+
|
|
189
|
+
{{#each tools}}
|
|
190
|
+
// {{method}} {{path}}
|
|
191
|
+
{{name}}Tool := mcp.NewTool("{{name}}",
|
|
192
|
+
mcp.WithDescription("{{escapeText description}}"),
|
|
193
|
+
{{#each params}}
|
|
194
|
+
mcp.With{{pascalGoType type}}("{{name}}",
|
|
195
|
+
mcp.Description("{{escapeText description}}"),
|
|
196
|
+
{{#if required}}
|
|
197
|
+
mcp.Required(),
|
|
198
|
+
{{/if}}
|
|
199
|
+
),
|
|
200
|
+
{{/each}}
|
|
201
|
+
)
|
|
202
|
+
s.AddTool({{name}}Tool, func(ctx context.Context, request mcp.CallToolRequest) (*mcp.CallToolResult, error) {
|
|
203
|
+
_ = request.Params.Arguments
|
|
204
|
+
// @@mcp-gen:start:{{name}}
|
|
205
|
+
{{#if http}}
|
|
206
|
+
return nil, fmt.Errorf("impl via client not yet wired: {{name}} (edit inside @@mcp-gen markers to call {{method}} {{path}})")
|
|
207
|
+
{{else if exampleResponse}}
|
|
208
|
+
return jsonSerialize(jsonExample(`{{json exampleResponse}}`))
|
|
209
|
+
{{else}}
|
|
210
|
+
return nil, fmt.Errorf("handler not implemented: {{name}}")
|
|
211
|
+
{{/if}}
|
|
212
|
+
// @@mcp-gen:end:{{name}}
|
|
213
|
+
})
|
|
214
|
+
|
|
215
|
+
{{/each}}
|
|
216
|
+
if err := server.ServeStdio(s); err != nil {
|
|
217
|
+
fmt.Fprintf(os.Stderr, "Fatal: %v\n", err)
|
|
218
|
+
os.Exit(1)
|
|
219
|
+
}
|
|
220
|
+
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
# {{info.title}} — MCP Server (Python)
|
|
2
|
+
|
|
3
|
+
> Generated by [mcp-gen](https://github.com/your-org/openapi-to-mcp)
|
|
4
|
+
> OpenAPI version: {{info.version}}
|
|
5
|
+
|
|
6
|
+
{{info.description}}
|
|
7
|
+
|
|
8
|
+
## Setup
|
|
9
|
+
|
|
10
|
+
```bash
|
|
11
|
+
pip install -r requirements.txt
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Run
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
python server.py
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Connect to Claude Desktop
|
|
21
|
+
|
|
22
|
+
Add to your `claude_desktop_config.json`:
|
|
23
|
+
|
|
24
|
+
```json
|
|
25
|
+
{
|
|
26
|
+
"mcpServers": {
|
|
27
|
+
"{{serverName}}": {
|
|
28
|
+
"command": "python",
|
|
29
|
+
"args": ["/absolute/path/to/server.py"]
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Available Tools ({{tools.length}})
|
|
36
|
+
|
|
37
|
+
{{#each tools}}
|
|
38
|
+
### `{{name}}`
|
|
39
|
+
|
|
40
|
+
> {{description}}
|
|
41
|
+
|
|
42
|
+
**`{{method}} {{path}}`**
|
|
43
|
+
|
|
44
|
+
{{#if params}}
|
|
45
|
+
| Name | Type | Required | Description |
|
|
46
|
+
|------|------|----------|-------------|
|
|
47
|
+
{{#each params}}
|
|
48
|
+
| `{{name}}` | `{{type}}` | {{#if required}}✓{{else}}—{{/if}} | {{description}} |
|
|
49
|
+
{{/each}}
|
|
50
|
+
{{else}}
|
|
51
|
+
No parameters.
|
|
52
|
+
{{/if}}
|
|
53
|
+
|
|
54
|
+
{{/each}}
|
|
55
|
+
|
|
56
|
+
## Implement Handlers
|
|
57
|
+
|
|
58
|
+
Find each function in `server.py` and replace the stub:
|
|
59
|
+
|
|
60
|
+
```python
|
|
61
|
+
@mcp.tool()
|
|
62
|
+
async def your_tool_name(param: str) -> Any:
|
|
63
|
+
"""Your tool description"""
|
|
64
|
+
# @@mcp-gen:start:your_tool_name
|
|
65
|
+
result = await your_api_call(param)
|
|
66
|
+
return result
|
|
67
|
+
# @@mcp-gen:end:your_tool_name
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
## Base URL
|
|
71
|
+
|
|
72
|
+
`{{baseUrl}}`
|
|
73
|
+
|
|
74
|
+
---
|
|
75
|
+
|
|
76
|
+
## Security
|
|
77
|
+
|
|
78
|
+
- **Don't embed credentials**: don't include provider keys or secrets directly in generated projects. Use environment variables or a secret manager (e.g., Vault, AWS Secrets Manager) or credential management service (e.g., Cohesivity.ai) for storage and secret revocation.
|
|
79
|
+
- **Scoped and TTL tokens**: prefer short-lived tokens with minimal scope, usage limits, and revocation capability.
|
|
80
|
+
- **Logs and audit**: log sensitive requests and enable audit for critical operations.
|
|
81
|
+
- **Never put secrets in model context**: avoid passing keys, tokens, or secrets in prompts or model context.
|
|
82
|
+
|
|
83
|
+
### Default `auth_context` contract
|
|
84
|
+
|
|
85
|
+
Generated tools expect **scoped authorization metadata** (not raw provider credentials), for example:
|
|
86
|
+
|
|
87
|
+
```json
|
|
88
|
+
{
|
|
89
|
+
"token_id": "tok_abc123",
|
|
90
|
+
"principal": "user:42",
|
|
91
|
+
"expires_at": "2026-05-11T14:00:00Z",
|
|
92
|
+
"allowed_tools": ["get_orders"],
|
|
93
|
+
"endpoint_allowlist": ["GET /orders"],
|
|
94
|
+
"spend_limit_usd": 5,
|
|
95
|
+
"spend_used_usd": 1.2,
|
|
96
|
+
"revoked": false,
|
|
97
|
+
"request_id": "req_01J..."
|
|
98
|
+
}
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
The scaffold blocks raw credential-style arguments such as `token`, `authorization`, `api_key`, `client_secret`, and similar keys by default.
|
|
102
|
+
|
|
103
|
+
*Quick example (env var):*
|
|
104
|
+
|
|
105
|
+
```bash
|
|
106
|
+
# Linux / macOS
|
|
107
|
+
export PROVIDER_KEY=your_key_here
|
|
108
|
+
|
|
109
|
+
# Windows (PowerShell)
|
|
110
|
+
$env:PROVIDER_KEY='your_key_here'
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
For advanced integrations and management, consider Cohesivity.ai as a backend for auth, storage, and revocation policies.
|
|
114
|
+
|
|
115
|
+
*Generated by mcp-gen — edit handlers, not the scaffold.*
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
|
|
15
|
+
- name: Set up Python
|
|
16
|
+
uses: actions/setup-python@v5
|
|
17
|
+
with:
|
|
18
|
+
python-version: "3.12"
|
|
19
|
+
|
|
20
|
+
- name: Install dependencies
|
|
21
|
+
run: pip install -r requirements.txt pytest pytest-asyncio
|
|
22
|
+
|
|
23
|
+
- name: Run tests
|
|
24
|
+
run: pytest --tb=short
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
# Auto-generated by mcp-gen v{{generatorVersion}} — do not edit.
|
|
2
|
+
# Spec: {{info.title}} v{{info.version}}
|
|
3
|
+
|
|
4
|
+
from pydantic import BaseModel, Field
|
|
5
|
+
from typing import Optional, List, Any, Literal
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
{{#each models}}
|
|
9
|
+
{{#if isEnum}}
|
|
10
|
+
{{name}} = {{#each enumValues}}{{literal this}}{{#unless @last}} | {{/unless}}{{/each}}
|
|
11
|
+
{{else}}
|
|
12
|
+
class {{name}}(BaseModel):
|
|
13
|
+
"""{{escapeText description}}"""
|
|
14
|
+
{{#each properties}}
|
|
15
|
+
{{name}}: {{pyType this}} {{#unless (includes ../required name)}} = None{{/unless}}
|
|
16
|
+
{{#if description}} # {{escapeText description}}{{/if}}
|
|
17
|
+
{{/each}}
|
|
18
|
+
|
|
19
|
+
{{/if}}
|
|
20
|
+
{{/each}}
|