Skip to main content
Glama
agaonker

MCP Server with OpenAI-Compatible Model Support

by agaonker
README.md
# MCP Server with OpenAI-Compatible Model Support

A small, readable [Model Context Protocol](https://modelcontextprotocol.io) server that exposes a handful of tools to any MCP client (Goose, Claude Desktop, OpenAI Agents, etc.). It supports both **stdio** and **streamable HTTP** transports, and one of its tools streams output from an OpenAI-compatible model.

## Tools

| Tool | Needs an LLM? | Description |
|------|---------------|-------------|
| `server_health` | No | Reports server status and provider config |
| `list_models` | No | Returns the configured model metadata |
| `echo` | No | Returns the provided message back |
| `list_directory` | No | Lists files/folders at a path on the host machine |
| `read_file` | No | Reads the text contents of a file on the host machine |
| `generate_text` | Yes | Streams text from the configured OpenAI-compatible model |

The filesystem tools (`list_directory`, `read_file`) only make sense over **stdio**, because the server runs on the user's own machine and can reach their disk — the canonical "local" MCP use case.

## Architecture

- `src/server.ts` — bootstraps the server, registers tools, selects the transport
- `src/config.ts` — loads environment configuration
- `src/providers/` — OpenAI-compatible client
- `src/tools/` — one file per tool

## Transports

Set `MCP_TRANSPORT` to choose how clients reach the server:

| Value | Behavior |
|-------|----------|
| `stdio` (default) | The client launches the server as a subprocess and talks over stdin/stdout. Local, single-client. |
| `http` | The server listens on a port (`MCP_HTTP_PORT`, default `3000`) in **stateless** mode, so it scales horizontally behind a load balancer. |
| `both` | Runs stdio and HTTP simultaneously in one process. |

`generate_text` streams its tokens as `notifications/message`. Over HTTP these ride the single request's own SSE response, so the server stays stateless (no session affinity needed).

## Setup

```bash
npm install
cp .env.example .env   # then fill in your provider settings
npm run build
npm start
```

Environment variables (see `.env.example`):

```bash
MCP_TRANSPORT=stdio              # stdio | http | both
MCP_HTTP_HOST=127.0.0.1
MCP_HTTP_PORT=3000
OPENAI_API_KEY=...               # only needed for generate_text
OPENAI_BASE_URL=https://api.openai.com/v1
OPENAI_MODEL=gpt-4o-mini
```

## Testing with Goose

[Goose](https://goose-docs.ai) is an open-source AI agent (by Block) that speaks MCP. It uses its own LLM as the "brain" to decide which of your tools to call. See the [Goose docs](https://goose-docs.ai) and [repo](https://github.com/block/goose).

### 1. Give Goose a brain (its own LLM)

Goose needs an LLM provider — this is **separate** from this server's `OPENAI_*` config. Using Anthropic as an example, run it inline so the values reach Goose:

```bash
export GOOSE_DISABLE_KEYRING=1        # read secrets from env instead of the OS keychain
export GOOSE_PROVIDER=anthropic
export GOOSE_MODEL=claude-sonnet-4-6
export ANTHROPIC_API_KEY=sk-ant-...
```

(Or run `goose configure` to store these in the keychain once.)

### 2. Launch a session with this server attached

```bash
npm run build
goose session --with-extension 'node /absolute/path/to/dist/server.js'
```

Goose calls MCP servers **extensions**; the tools appear namespaced (e.g. `echo`, `list_directory`).

To test the **HTTP** transport instead, start the server first and point Goose at the URL:

```bash
# terminal 1
MCP_TRANSPORT=http MCP_HTTP_PORT=3000 node dist/server.js
# terminal 2
goose session --with-streamable-http-extension 'http://127.0.0.1:3000/'
```

### 3. Ask in plain English — the brain picks the tool

You don't name the tool; Goose decides:

```
echo the words "it works"
list the files in /absolute/path/to/src/tools
read the package.json in this project and tell me the version
what model is the server configured to use?
is the server healthy?
```

> Note: the agent may sometimes prefer one of its **own** built-in tools over yours
> (e.g. it may run a shell `ls` instead of `list_directory`). That's the brain
> choosing — phrase the request toward your tool, or disable conflicting built-ins,
> if you want to force it.

### Screenshots

`echo` — Goose's brain calls the `echo` tool and reports the result:

![Goose calling the echo tool](docs/screenshots/echo-test.png)

Listing files in the project:

![Goose listing files](docs/screenshots/list-files-test.png)

## Inspecting without an agent

To poke the server directly (no LLM needed):

```bash
# Browser UI:
npx @modelcontextprotocol/inspector node dist/server.js

# Raw JSON-RPC over HTTP (server in http mode):
curl -s -X POST http://127.0.0.1:3000/ \
  -H 'Content-Type: application/json' \
  -H 'Accept: application/json, text/event-stream' \
  -d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
```

## Example stdio client config

```json
{
  "mcpServers": {
    "mcp-openai-server": {
      "command": "node",
      "args": ["/absolute/path/to/dist/server.js"],
      "env": {
        "OPENAI_API_KEY": "your-key",
        "OPENAI_BASE_URL": "https://api.openai.com/v1",
        "OPENAI_MODEL": "gpt-4o-mini"
      }
    }
  }
}
```

TDQS

B3.1/5.0

Scored across 6 tools

Disambiguation5/5

All six tools have clearly distinct purposes: echo is for testing, generate_text for model generation, list_directory and read_file for file operations, list_models for model metadata, and server_health for checking configuration. There is no overlap or ambiguity.

Naming Consistency3/5

Most tools follow a verb_noun pattern (generate_text, list_directory, list_models, read_file), but echo is a single verb and server_health is a noun phrase, breaking consistency. The pattern is still readable but not uniform.

Tool Count4/5

With six tools, the count is reasonable for an MCP server. However, the server's stated focus on OpenAI-compatible model support is somewhat diluted by the inclusion of file system tools (list_directory, read_file), which slightly reduces the sense of a well-scoped set.

Completeness2/5

For a server claiming OpenAI-compatible model support, the tool surface is notably sparse: only a single generate_text tool and a list_models tool. Missing capabilities like context management, streaming, or role-based chat severely limit model interaction. File access is also one-sided (read only).

Maintenance

ActivityInactive
ResponsivenessNo issues