Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
61 changes: 61 additions & 0 deletions docs/modules/fxmcpserver.md
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ It comes with:
- automatic requests logging and tracing (method, target, duration, ...)
- automatic requests metrics (count and duration)
- possibility to register MCP resources, resource templates, prompts and tools
- possibility to register MCP SSE server context hooks
- possibility to expose the MCP server via Stdio (local) and/or HTTP SSE (remote)

## Installation
Expand Down Expand Up @@ -516,6 +517,66 @@ modules:
tools: true # to expose MCP tools (disabled by default)
```

## Hooks

This module offers the possibility to provide context hooks with [MCPSSEServerContextHook](https://github.com/ankorstore/yokai/blob/main/fxmcpserver/server/sse/context.go) implementations, that will be applied on each MCP SSE request.

For example, an MCP SSE server context hook that adds a config value to the context:

```go title="internal/mcp/resource/readme.go"
package hook

import (
"context"
"net/http"

"github.com/ankorstore/yokai/config"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
)

type ExampleHook struct {
config *config.Config
}

func NewExampleHook(config *config.Config) *ExampleHook {
return &ExampleHook{
config: config,
}
}

func (h *ExampleHook) Handle() server.SSEContextFunc {
return func(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, "foo", h.config.GetString("foo"))
}
}
```

You can register your MCP SSE server context hook:

- with `AsMCPSSEServerContextHook()` to register a single MCP SSE server context hook
- with `AsMCPSSEServerContextHooks()` to register several MCP SSE server context hooks at once

```go title="internal/register.go"
package internal

import (
"github.com/ankorstore/yokai/fxmcpserver"
"github.com/foo/bar/internal/mcp/hook"
"go.uber.org/fx"
)

func Register() fx.Option {
return fx.Options(
// registers ReadmeResource as MCP resource
fxmcpserver.AsMCPSSEServerContextHook(hook.NewExampleHook),
// ...
)
}
```

The dependencies of your MCP SSE server context hooks will be autowired.

## Logging

You can configure the MCP server requests and responses automatic logging:
Expand Down
94 changes: 47 additions & 47 deletions fxmcpserver/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,16 +12,16 @@
* [Installation](#installation)
* [Features](#features)
* [Documentation](#documentation)
* [Dependencies](#dependencies)
* [Loading](#loading)
* [Configuration](#configuration)
* [Registration](#registration)
* [Resources](#resources)
* [Resource templates](#resource-templates)
* [Prompts](#prompts)
* [Tools](#tools)
* [Hooks](#hooks)
* [Testing](#testing)
* [Dependencies](#dependencies)
* [Loading](#loading)
* [Configuration](#configuration)
* [Registration](#registration)
* [Resources](#resources)
* [Resource templates](#resource-templates)
* [Prompts](#prompts)
* [Tools](#tools)
* [Hooks](#hooks)
* [Testing](#testing)
<!-- TOC -->

## Installation
Expand Down Expand Up @@ -109,7 +109,7 @@ modules:
prompts: true # to expose MCP prompts (disabled by default)
tools: true # to expose MCP tools (disabled by default)
transport:
sse:
sse:
expose: true # to remotely expose the MCP server via SSE (disabled by default)
address: ":8082" # exposition address (":8082" by default)
base_url: "" # base url ("" by default)
Expand Down Expand Up @@ -250,7 +250,7 @@ package main

import (
"context"

"github.com/foo/bar/internal/user"

"github.com/ankorstore/yokai/config"
Expand Down Expand Up @@ -553,59 +553,59 @@ modules:
```
### Hooks

This module offers the possibility to provide context hooks with [MCPSSEServerContextHook](server/sse/context.go) implementations, applied on each MCP SSE request.
This module offers the possibility to provide context hooks with [MCPSSEServerContextHook](server/sse/context.go) implementations, that will be applied on each MCP SSE request.

You can use the `AsMCPSSEServerMiddleware()` function to register an MCP SSE server middleware, or `AsMCPSSEServerMiddlewares()` to register several MCP SSE server middlewares at once.
You can use the `AsMCPSSEServerContextHook()` function to register an MCP SSE server context hook, or `AsMCPSSEServerContextHooks()` to register several MCP SSE server context hooks at once.

The dependencies of your MCP SSE server middlewares will be autowired.
The dependencies of your MCP SSE server context hooks will be autowired.

```go
package main

import (
"context"
"net/http"

"github.com/ankorstore/yokai/config"
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxgenerate"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmcpserver"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/ankorstore/yokai/fxtrace"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"go.uber.org/fx"
"context"
"net/http"

"github.com/ankorstore/yokai/config"
"github.com/ankorstore/yokai/fxconfig"
"github.com/ankorstore/yokai/fxgenerate"
"github.com/ankorstore/yokai/fxlog"
"github.com/ankorstore/yokai/fxmcpserver"
"github.com/ankorstore/yokai/fxmetrics"
"github.com/ankorstore/yokai/fxtrace"
"github.com/mark3labs/mcp-go/mcp"
"github.com/mark3labs/mcp-go/server"
"go.uber.org/fx"
)

type ExampleHook struct {
config *config.Config
config *config.Config
}

func NewExampleHook(config *config.Config) *ExampleHook {
return &ExampleHook{
config: config,
}
return &ExampleHook{
config: config,
}
}

func (r *ExampleHook) Handle() server.SSEContextFunc {
return func(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, "foo", "bar")
}
func (h *ExampleHook) Handle() server.SSEContextFunc {
return func(ctx context.Context, r *http.Request) context.Context {
return context.WithValue(ctx, "foo", h.config.GetString("foo"))
}
}

func main() {
fx.New(
fxconfig.FxConfigModule,
fxlog.FxLogModule,
fxtrace.FxTraceModule,
fxmetrics.FxMetricsModule,
fxgenerate.FxGenerateModule,
fxmcpserver.FxMCPServerModule,
fx.Options(
fxmcpserver.AsMCPSSEServerContextHook(NewExampleHook), // registers the NewExampleHook as MCP SSE server context hook
),
).Run()
fx.New(
fxconfig.FxConfigModule,
fxlog.FxLogModule,
fxtrace.FxTraceModule,
fxmetrics.FxMetricsModule,
fxgenerate.FxGenerateModule,
fxmcpserver.FxMCPServerModule,
fx.Options(
fxmcpserver.AsMCPSSEServerContextHook(NewExampleHook), // registers the NewExampleHook as MCP SSE server context hook
),
).Run()
}
```

Expand Down