To embed MCP functionality within a larger application, encapsulate the MCPServer and InProcessClient within your application's struct. This allows your application to expose its own internal state or configuration via MCP tools while simultaneously using the MCP client to call other tools (even those hosted on the same server) for internal processing.
Key steps:
- Define an application struct containing
*server.MCPServer and *client.InProcessClient. - Initialize the server and client in your constructor.
- Register tools that interact with your application's internal state (e.g.,
get_app_status or update_config). - Use the client within application methods to invoke tools via
app.mcpClient.CallTool.
// Embedded MCP server in a larger application
type Application struct {
mcpServer *server.MCPServer
mcpClient *client.InProcessClient
config *Config
}
func NewApplication(config *Config) *Application {
app := &Application{
config: config,
}
// Create embedded MCP server
app.mcpServer = server.NewMCPServer("Embedded Server", "1.0.0",
server.WithToolCapabilities(true),
)
// Add application-specific tools
app.addApplicationTools()
// Create in-process client for internal use
var err error
app.mcpClient, err = client.NewInProcessClient(app.mcpServer)
if err != nil {
panic(err)
}
return app
}
type Config struct {
AppName string
Debug bool
}
func (app *Application) addApplicationTools() {
// Application status tool
app.mcpServer.AddTool(
mcp.NewTool("get_app_status",
mcp.WithDescription("Get current application status"),
),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
return mcp.NewToolResultText(fmt.Sprintf(`{"app_name":"%s","debug":%t,"status":"running"}`,
app.config.AppName, app.config.Debug)), nil
},
)
// Configuration tool
app.mcpServer.AddTool(
mcp.NewTool("update_config",
mcp.WithDescription("Update application configuration"),
mcp.WithString("key", mcp.Required()),
mcp.WithString("value", mcp.Required()),
),
func(ctx context.Context, req mcp.CallToolRequest) (*mcp.CallToolResult, error) {
key := req.GetString("key", "")
value := req.GetString("value", "")
// Update configuration based on key
switch key {
case "debug":
app.config.Debug = value == "true"
case "app_name":
app.config.AppName = value
default:
return mcp.NewToolResultError(fmt.Sprintf("unknown config key: %s", key)), nil
}
return mcp.NewToolResultText(fmt.Sprintf("Updated %s to %s", key, value)), nil
},
)
}
func (app *Application) ProcessWithMCP(ctx context.Context, operation string) (interface{}, error) {
// Use MCP tools internally for processing
result, err := app.mcpClient.CallTool(ctx, mcp.CallToolRequest{
Params: mcp.CallToolParams{
Name: "calculate",
Arguments: map[string]interface{}{
"operation": operation,
"x": 10.0,
"y": 5.0,
},
},
})
if err != nil {
return nil, err
}
// Extract text from the first content item
if len(result.Content) > 0 {
if textContent, ok := mcp.AsTextContent(result.Content[0]); ok {
return textContent.Text, nil
}
}
return "no result", nil
}
// Usage example
func main() {
config := &Config{
AppName: "My App",
Debug: true,
}
app := NewApplication(config)
ctx := context.Background()
// Initialize the embedded MCP client
_, err := app.mcpClient.Initialize(ctx, mcp.InitializeRequest{
Params: mcp.InitializeRequestParams{
ProtocolVersion: "2024-11-05",
Capabilities: mcp.ClientCapabilities{
Tools: &mcp.ToolsCapability{},
},
ClientInfo: mcp.Implementation{
Name: "embedded-client",
Version: "1.0.0",
},
},
})
if err != nil {
log.Fatal(err)
}
// Use MCP functionality within the application
result, err := app.ProcessWithMCP(ctx, "add")
if err != nil {
log.Fatal(err)
}
fmt.Printf("Application result: %v\n", result)
}