Files
Zach Wasserman 6726bb196f Add mcp_listening_servers table (#34286)
**Related issue:** Resolves #34330

# Checklist for submitter

If some of the following don't apply, delete the relevant line.

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
See [Changes
files](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/guides/committing-changes.md#changes-files)
for more information.
- [x] Input data is properly validated, `SELECT *` is avoided, SQL
injection is prevented (using placeholders for values in statements)

## Testing

- [x] Added/updated automated tests

- [x] QA'd all new/changed functionality manually (so far just macOS)

## fleetd/orbit/Fleet Desktop

- [x] Verified compatibility with the latest released version of Fleet
(see [Must
rule](https://github.com/fleetdm/fleet/blob/main/docs/Contributing/workflows/fleetd-development-and-release-strategy.md))
- [x] Verified that fleetd runs on macOS, Linux and Windows


<!-- This is an auto-generated comment: release notes by coderabbit.ai
-->
## Summary by CodeRabbit

* **New Features**
* Added a built-in mcp_listening_servers table to discover MCP servers
by inspecting listening ports and probing endpoints; returns process
info, server metadata, capabilities, tools, prompts, and resources
(supports macOS, Windows, Linux).

* **Tests**
* Added comprehensive unit tests covering detection, IPv6 handling, SSE
responses, and session lifecycle.
<!-- end of auto-generated comment: release notes by coderabbit.ai -->
2025-10-21 11:25:11 -07:00

67 lines
1.7 KiB
Go

package main
import (
"flag"
"fmt"
"log"
"os"
"time"
"github.com/cenkalti/backoff/v4"
orbittable "github.com/fleetdm/fleet/v4/orbit/pkg/table"
"github.com/osquery/osquery-go"
)
var (
socket = flag.String("socket", "", "Path to the extensions UNIX domain socket")
timeout = flag.Int("timeout", 3, "Seconds to wait for autoloaded extensions")
interval = flag.Int("interval", 3, "Seconds delay between connectivity checks")
// verbose must be set because osqueryd will set it on the extension when running in verbose mode.
_ = flag.Bool("verbose", false, "Enable verbose informational messages")
)
func main() {
flag.Parse()
if *socket == "" {
log.Fatalf(`Usage: %s -socket SOCKET_PATH`, os.Args[0])
}
serverTimeout := osquery.ServerTimeout(
time.Second * time.Duration(*timeout),
)
serverPingInterval := osquery.ServerPingInterval(
time.Second * time.Duration(*interval),
)
var server *osquery.ExtensionManagerServer
backOff := backoff.WithMaxRetries(backoff.NewConstantBackOff(time.Millisecond*200), 25) // retry once per 200ms for 25 times == 5 seconds
op := func() error {
s, err := osquery.NewExtensionManagerServer("com.fleetdm.fleetd_tables.osquery_extension.v1", *socket, serverTimeout, serverPingInterval)
if err != nil {
return fmt.Errorf("error creating extension: %w", err)
}
server = s
return nil
}
err := backoff.Retry(op, backOff)
if err != nil {
log.Fatalln(err)
}
opts := orbittable.PluginOpts{
Socket: *socket,
}
plugins := orbittable.OrbitDefaultTables(opts)
platformTables, err := orbittable.PlatformTables(opts)
if err != nil {
log.Fatalln(err)
}
plugins = append(plugins, platformTables...)
server.RegisterPlugin(plugins...)
if err := server.Run(); err != nil {
log.Fatalln(err)
}
}