chore: initial release v0.1.0
ci / test (push) Failing after 5s
ci / lint (push) Failing after 4s

forge-toolkit: хелперы для разработки MCP-серверов на Go.

- toolkit: args/schema/result/env/probe/server
- toolkit/validate: валидаторы синтаксиса
- configreload: live-reload конфига по контент-хэшу
- docs: ARCHITECTURE, quickstart, mcp-contract
- template/ и examples/hello-tool/
- CI (Gitea Actions), лицензия Apache-2.0
This commit is contained in:
Maksim Totmin
2026-09-14 00:23:08 +07:00
commit c398045664
43 changed files with 2581 additions and 0 deletions
+27
View File
@@ -0,0 +1,27 @@
# template
Каркас MCP-сервера на `forge-toolkit`: конфиг, потокобезопасный `Manager`,
один инструмент и корректный lifecycle.
## Как пользоваться
1. Скопируйте каталог и переименуйте модуль в `go.mod`
(`example.com/forge-tool` → ваш путь).
2. Замените доменную логику в `internal/tools/tools.go`.
3. Соберите и проверьте:
```bash
go mod tidy
go build -o forge-tool .
./forge-tool --health # → ok
```
## Подключение
Любой MCP-клиент — как обычный stdio-сервер:
```json
{ "mcpServers": { "forge-tool": { "command": "/abs/path/forge-tool", "args": ["-config", "/abs/path/config.json"] } } }
```
Контракт и правила — [`../docs/ARCHITECTURE.md`](../docs/ARCHITECTURE.md).
+3
View File
@@ -0,0 +1,3 @@
{
"greeting": "hello"
}
+21
View File
@@ -0,0 +1,21 @@
module example.com/forge-tool
go 1.27.0
require (
git.totmin.ru/en2zmax/forge-toolkit v0.1.0
github.com/modelcontextprotocol/go-sdk v1.7.0
)
require (
github.com/google/jsonschema-go v0.4.3 // indirect
github.com/segmentio/asm v1.1.3 // indirect
github.com/segmentio/encoding v0.5.4 // indirect
github.com/yosida95/uritemplate/v3 v3.0.2 // indirect
golang.org/x/oauth2 v0.35.0 // indirect
golang.org/x/sync v0.20.0 // indirect
golang.org/x/sys v0.41.0 // indirect
golang.org/x/time v0.15.0 // indirect
)
replace git.totmin.ru/en2zmax/forge-toolkit => ..
+24
View File
@@ -0,0 +1,24 @@
github.com/golang-jwt/jwt/v5 v5.3.1 h1:kYf81DTWFe7t+1VvL7eS+jKFVWaUnK9cB1qbwn63YCY=
github.com/golang-jwt/jwt/v5 v5.3.1/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE=
github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
github.com/google/jsonschema-go v0.4.3 h1:/DBOLZTfDow7pe2GmaJNhltueGTtDKICi8V8p+DQPd0=
github.com/google/jsonschema-go v0.4.3/go.mod h1:r5quNTdLOYEz95Ru18zA0ydNbBuYoo9tgaYcxEYhJVE=
github.com/modelcontextprotocol/go-sdk v1.7.0 h1:yqjY2dsbKAC0LSuWZVBMrHgiG8ukXv6NRo0JiALay44=
github.com/modelcontextprotocol/go-sdk v1.7.0/go.mod h1:dL7u98E/zjJTGzEq+j30jQ8K2k1mb6LeAH4inEcSGts=
github.com/segmentio/asm v1.1.3 h1:WM03sfUOENvvKexOLp+pCqgb/WDjsi7EK8gIsICtzhc=
github.com/segmentio/asm v1.1.3/go.mod h1:Ld3L4ZXGNcSLRg4JBsZ3//1+f/TjYl0Mzen/DQy1EJg=
github.com/segmentio/encoding v0.5.4 h1:OW1VRern8Nw6ITAtwSZ7Idrl3MXCFwXHPgqESYfvNt0=
github.com/segmentio/encoding v0.5.4/go.mod h1:HS1ZKa3kSN32ZHVZ7ZLPLXWvOVIiZtyJnO1gPH1sKt0=
github.com/yosida95/uritemplate/v3 v3.0.2 h1:Ed3Oyj9yrmi9087+NczuL5BwkIc4wvTb5zIM+UJPGz4=
github.com/yosida95/uritemplate/v3 v3.0.2/go.mod h1:ILOh0sOhIJR3+L/8afwt/kE++YT040gmv5BQTMR2HP4=
golang.org/x/oauth2 v0.35.0 h1:Mv2mzuHuZuY2+bkyWXIHMfhNdJAdwW3FuWeCPYN5GVQ=
golang.org/x/oauth2 v0.35.0/go.mod h1:lzm5WQJQwKZ3nwavOZ3IS5Aulzxi68dUSgRHujetwEA=
golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.41.0 h1:Ivj+2Cp/ylzLiEU89QhWblYnOE9zerudt9Ftecq2C6k=
golang.org/x/sys v0.41.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.42.0 h1:uNgphsn75Tdz5Ji2q36v/nsFSfR/9BRFvqhGBaJGd5k=
golang.org/x/tools v0.42.0/go.mod h1:Ma6lCIwGZvHK6XtgbswSoWroEkhugApmsXyrUmBhfr0=
+61
View File
@@ -0,0 +1,61 @@
// Package tools — доменный слой сервера: конфиг, Manager и инструменты.
// Замените содержимое на свою задачу; каркасные хелперы берите из toolkit.
package tools
import (
"context"
"encoding/json"
"os"
"strings"
"git.totmin.ru/en2zmax/forge-toolkit"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
// Config — доменный конфиг сервера (пример). Замените поля на свои.
type Config struct {
Greeting string `json:"greeting"`
}
// LoadConfig читает config.json и разворачивает ${VAR}. Пустые обязательные
// поля лучше отвергать здесь (fail-closed), а не в обработчике.
func LoadConfig(path string) (*Config, error) {
raw, err := os.ReadFile(path)
if err != nil {
return nil, err
}
var cfg Config
if err := json.Unmarshal(toolkit.Expand(raw), &cfg); err != nil {
return nil, err
}
if strings.TrimSpace(cfg.Greeting) == "" {
cfg.Greeting = "hello"
}
return &cfg, nil
}
// Manager — доменное состояние сервера. Инструменты вызываются конкурентно,
// поэтому изменяемое состояние держите под sync.RWMutex.
type Manager struct {
cfg *Config
}
// NewManager создаёт Manager.
func NewManager(cfg *Config) *Manager { return &Manager{cfg: cfg} }
// Close освобождает ресурсы (соединения, файлы). В каркасе — no-op.
func (m *Manager) Close() {}
// RegisterAll регистрирует инструменты сервера.
func RegisterAll(s *mcp.Server, mgr *Manager) {
mcp.AddTool(s, &mcp.Tool{
Name: "greet",
Description: "Приветствие по имени. Без побочных эффектов.",
InputSchema: toolkit.Schema(map[string]any{
"name": toolkit.StrProps("имя", false),
}, nil),
}, func(_ context.Context, req *mcp.CallToolRequest, _ struct{}) (*mcp.CallToolResult, any, error) {
name := toolkit.GetString(toolkit.RequestArgs(req), "name", "world")
return toolkit.Text(mgr.cfg.Greeting + ", " + name + "!"), nil, nil
})
}
+42
View File
@@ -0,0 +1,42 @@
// Command forge-tool — каркас MCP-сервера на forge-toolkit.
//
// Скопируй каталог, переименуй модуль (go.mod) и замени доменную логику в
// internal/tools на свою. Точка входа — toolkit.Health (дешёвый --health) и
// toolkit.Run (stdio-цикл с graceful shutdown).
package main
import (
"flag"
"fmt"
"os"
"example.com/forge-tool/internal/tools"
"git.totmin.ru/en2zmax/forge-toolkit"
"github.com/modelcontextprotocol/go-sdk/mcp"
)
func main() {
if toolkit.Health() {
return
}
configPath := flag.String("config", "config.json", "путь к config.json")
flag.Parse()
cfg, err := tools.LoadConfig(*configPath)
if err != nil {
fmt.Fprintf(os.Stderr, "forge-tool: config: %v\n", err)
os.Exit(1)
}
mgr := tools.NewManager(cfg)
defer mgr.Close()
if err := toolkit.Run("forge-tool", func(s *mcp.Server) {
tools.RegisterAll(s, mgr)
}); err != nil {
fmt.Fprintf(os.Stderr, "forge-tool: %v\n", err)
os.Exit(1)
}
}