Skip to content

Commit b1960e8

Browse files
committed
feat: skip code uploads
1 parent 15325bf commit b1960e8

File tree

13 files changed

+277
-42
lines changed

13 files changed

+277
-42
lines changed

.golangci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,7 +47,7 @@ linters:
4747
- 15
4848
- name: cognitive-complexity
4949
arguments:
50-
- 15 # Yes, I'm aware this is insane, but whatever, this is a pet project
50+
- 16 # Yes, I'm aware this is insane, but whatever, this is a pet project
5151
- name: max-public-structs
5252
arguments:
5353
- 10 # Up from 5

README.md

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ Download the latest release from the [releases page](https://github.com/cyclimse
1414

1515
Run the MCP server:
1616

17-
```console
17+
```bash
1818
./mcp-scaleway-functions
1919
```
2020

@@ -97,7 +97,7 @@ Further configuration can be done via the
9797

9898
For instance, you can set a region to work in via the `SCW_DEFAULT_REGION` environment variable.
9999

100-
```console
100+
```bash
101101
SCW_DEFAULT_REGION=nl-ams ./mcp-scaleway-functions
102102
```
103103

@@ -129,13 +129,12 @@ Logs are stored in the `$XDG_STATE_HOME/mcp-scaleway-functions` directory (usual
129129

130130
Running tests:
131131

132-
```console
132+
```bash
133133
go tool gotestsum --format testdox
134134
```
135135

136136
Generating mocks:
137137

138-
```console
138+
```bash
139139
go tool mockery
140140
```
141-

internal/constants/constants.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,9 @@ const (
1414
// It's used as an extra safety measure to avoid deleting resources not created by this tool.
1515
TagCreatedByScalewayMCP = "created_by=" + ProjectName
1616

17-
// TagCodeArchiveDigest is the tag key used to store the code archive digest.
17+
// TagCodeArchiveDigestPrefix is the tag key used to store the code archive digest.
1818
// It's used to avoid redeploying the same code.
19-
TagCodeArchiveDigest = "code_archive_digest="
19+
TagCodeArchiveDigestPrefix = "code_archive_digest="
2020

2121
// RequiredPermissionSets is the minimum permission sets required for the Scaleway API key.
2222
RequiredPermissionSets = "FunctionsFullAccess, ObservabilityFullAccess"

internal/middlewares/middlewares.go

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,12 @@ func NewLogging() mcp.Middleware {
4747
logger = logger.With(slog.Duration("duration", duration))
4848
logger = logger.With(slogAttrFromResult(result)...)
4949

50-
if err != nil {
50+
switch {
51+
case err != nil:
5152
logger.Error("Request failed", slog.String("error", err.Error()))
52-
} else {
53+
case isErrorResult(result):
54+
logger.Error("Request errored")
55+
default:
5356
logger.Info("Request succeeded")
5457
}
5558

@@ -73,11 +76,28 @@ func slogAttrFromRequest(method string, req mcp.Request) []any {
7376
return attrs
7477
}
7578

79+
func isErrorResult(result mcp.Result) bool {
80+
if callResult, ok := result.(*mcp.CallToolResult); ok {
81+
return callResult.IsError
82+
}
83+
84+
return false
85+
}
86+
7687
func slogAttrFromResult(result mcp.Result) []any {
7788
attrs := []any{}
7889

7990
if callResult, ok := result.(*mcp.CallToolResult); ok {
8091
attrs = append(attrs, slogJSON("tool_result", callResult.StructuredContent))
92+
93+
if callResult.IsError {
94+
attrs = append(attrs, slog.Bool("tool_error", true))
95+
96+
if len(callResult.Content) > 0 {
97+
firstLines := callResult.Content[0]
98+
attrs = append(attrs, slogJSON("tool_error_content", firstLines))
99+
}
100+
}
81101
}
82102

83103
return attrs

internal/scaleway/cockpit/entry.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -21,14 +21,11 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
2121
_, err := jsonparser.ArrayEach(
2222
data,
2323
func(value []byte, t jsonparser.ValueType, _ int, _ error) {
24-
// assert that both items in array are of type string
24+
// Assert that both items in array are of type string
2525
switch i {
2626
case 0: // timestamp
2727
if t != jsonparser.String {
28-
parseError = fmt.Errorf(
29-
"%w: expected string timestamp",
30-
jsonparser.MalformedStringError,
31-
)
28+
parseError = jsonparser.MalformedStringError
3229

3330
return
3431
}
@@ -41,7 +38,7 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
4138
}
4239

4340
e.Timestamp = time.Unix(0, ts)
44-
case 1: // value
41+
case 1: // log line
4542
if t != jsonparser.String {
4643
parseError = jsonparser.MalformedStringError
4744

@@ -57,8 +54,7 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
5754

5855
e.Line = v
5956
default:
60-
// Ignore extra values
61-
return
57+
return // no-op
6258
}
6359

6460
i++
@@ -69,5 +65,9 @@ func (e *Entry) UnmarshalJSON(data []byte) error {
6965
return parseError
7066
}
7167

72-
return fmt.Errorf("parsing log entry array: %w", err)
68+
if err != nil {
69+
return fmt.Errorf("parsing log entry array: %w", err)
70+
}
71+
72+
return nil
7373
}

internal/scaleway/create_and_deploy_function_namespace.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ type CreateAndDeployFunctionNamespace struct {
2626
func (in CreateAndDeployFunctionNamespace) ToSDK() *function.CreateNamespaceRequest {
2727
return &function.CreateNamespaceRequest{
2828
Name: in.Name,
29-
Tags: setCreatedByTagIfAbsent(in.Tags),
29+
Tags: setCreatedByTag(in.Tags),
3030
}
3131
}
3232

internal/scaleway/create_deploy_function.go

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,6 @@ import (
55
"fmt"
66
"time"
77

8-
"github.com/cyclimse/mcp-scaleway-functions/internal/constants"
98
"github.com/modelcontextprotocol/go-sdk/mcp"
109
function "github.com/scaleway/scaleway-sdk-go/api/function/v1beta1"
1110
"github.com/scaleway/scaleway-sdk-go/scw"
@@ -82,7 +81,7 @@ func (req CreateAndDeployFunctionRequest) ToSDK(
8281
Seconds: int64(timeout.Seconds()),
8382
},
8483
Description: &req.Description,
85-
Tags: setCreatedByTagIfAbsent(req.Tags),
84+
Tags: setCreatedByTag(req.Tags),
8685
EnvironmentVariables: &req.EnvironmentVariables,
8786
SecretEnvironmentVariables: secrets,
8887
MinScale: req.MinScale,
@@ -91,6 +90,7 @@ func (req CreateAndDeployFunctionRequest) ToSDK(
9190
}, nil
9291
}
9392

93+
//nolint:funlen
9494
func (t *Tools) CreateAndDeployFunction(
9595
ctx context.Context,
9696
req *mcp.CallToolRequest,
@@ -122,7 +122,7 @@ func (t *Tools) CreateAndDeployFunction(
122122
return nil, Function{}, fmt.Errorf("creating archive: %w", err)
123123
}
124124

125-
tags := append(fun.Tags, constants.TagCodeArchiveDigest+archive.Digest)
125+
tags := setCodeArchiveDigestTag(fun.Tags, archive.Digest)
126126

127127
// However, as a side-effect of doing creation first, we need to
128128
// update the function to add the code archive digest tag (which helps
@@ -133,7 +133,10 @@ func (t *Tools) CreateAndDeployFunction(
133133
Tags: scw.StringsPtr(tags),
134134
}, scw.WithContext(ctx))
135135
if err != nil {
136-
return nil, Function{}, fmt.Errorf("updating function with code archive digest tag: %w", err)
136+
return nil, Function{}, fmt.Errorf(
137+
"updating function with code archive digest tag: %w",
138+
err,
139+
)
137140
}
138141

139142
presignedURLResp, err := t.functionsAPI.GetFunctionUploadURL(

internal/scaleway/fetch_function_logs.go

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,11 @@ import (
44
"context"
55
"errors"
66
"fmt"
7-
"log/slog"
87
"strings"
98
"time"
109

1110
"github.com/cyclimse/mcp-scaleway-functions/internal/scaleway/cockpit"
11+
"github.com/cyclimse/mcp-scaleway-functions/pkg/slogctx"
1212
"github.com/modelcontextprotocol/go-sdk/mcp"
1313
)
1414

@@ -37,6 +37,8 @@ func (t *Tools) FetchFunctionLogs(
3737
_ *mcp.CallToolRequest,
3838
req FetchFunctionLogsRequest,
3939
) (*mcp.CallToolResult, FetchFunctionLogsResponse, error) {
40+
logger := slogctx.FromContext(ctx)
41+
4042
function, ns, err := getFunctionAndNamespaceByFunctionName(
4143
ctx,
4244
t.functionsAPI,
@@ -47,7 +49,7 @@ func (t *Tools) FetchFunctionLogs(
4749
}
4850

4951
if ns.ProjectID != t.projectID {
50-
slog.WarnContext(
52+
logger.WarnContext(
5153
ctx,
5254
"fetching logs across multiple Scaleway projects is not supported yet",
5355
"function_project_id", ns.ProjectID,

internal/scaleway/helpers.go

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -80,14 +80,36 @@ func getFunctionAndNamespaceByFunctionName(
8080
return fun, ns, nil
8181
}
8282

83-
func setCreatedByTagIfAbsent(tags []string) []string {
84-
if !slices.Contains(tags, constants.TagCreatedByScalewayMCP) {
85-
tags = append(tags, constants.TagCreatedByScalewayMCP)
83+
func setTag(tags []string, tag string) []string {
84+
if !slices.Contains(tags, tag) {
85+
tags = append(tags, tag)
8686
}
8787

8888
return tags
8989
}
9090

91+
func setCreatedByTag(tags []string) []string {
92+
return setTag(tags, constants.TagCreatedByScalewayMCP)
93+
}
94+
95+
func setCodeArchiveDigestTag(tags []string, digest string) []string {
96+
prefix := constants.TagCodeArchiveDigestPrefix
97+
98+
// Remove any existing digest tag.
99+
filtered := make([]string, 0, len(tags))
100+
101+
for _, tag := range tags {
102+
if !strings.HasPrefix(tag, prefix) {
103+
filtered = append(filtered, tag)
104+
}
105+
}
106+
107+
// Add the new digest tag.
108+
filtered = append(filtered, prefix+digest)
109+
110+
return filtered
111+
}
112+
91113
func checkResourceOwnership(tags []string) error {
92114
if !slices.Contains(tags, constants.TagCreatedByScalewayMCP) {
93115
return fmt.Errorf("%w: resource does not belong to this tool", ErrResourceNotOwnedByTool)
@@ -97,7 +119,7 @@ func checkResourceOwnership(tags []string) error {
97119
}
98120

99121
func getCodeArchiveDigestFromTags(tags []string) (string, bool) {
100-
prefix := constants.TagCodeArchiveDigest
122+
prefix := constants.TagCodeArchiveDigestPrefix
101123

102124
for _, tag := range tags {
103125
after, found := strings.CutPrefix(tag, prefix)

0 commit comments

Comments
 (0)