Skip to content
kamathPublic

About

Resources

Stars

6 stars

Watchers

0 watching

Forks

Repository files navigation

Hookfish

A browser-first client for browsing, configuring, and running executables from pluggable sources. OpenAPI is the built-in source adapter. Connected-source metadata and keys live in the browser; the server provides suggested sources, fetches source documents, and runs proxied invocations.

This repository is a pnpm/Turborepo workspace:

  • packages/app contains the reusable Vite client application and TanStack Router. Its mountApp export accepts an API base URL.
  • examples/ holds four standalone TanStack Start shells that mount the client application: cloudflare, vercel, node, and docker. See examples/README.md.
  • packages/api is a mountable Hono API with OpenAPI docs and Hono RPC. TanStack Start forwards /api/* into it.
  • packages/cli builds the hookfish npm package and bundles examples/node as its web app.
pnpm install
pnpm dev

Open http://localhost:3000. The launcher groups suggested sources into one pane per category. Paste any other URL in the bar and press Enter; Hookfish probes the URL to decide whether it is an MCP server or an OpenAPI document. Type a name instead of a URL to filter the suggestions, or search the web for a remote MCP server (needs EXA_API_KEY).

Embedding the client

@hookfish/app and @hookfish/api build as independently consumable packages. A deployment repository can mount the client without importing this workspace's source:

import { mountApp } from '@hookfish/app'
import '@hookfish/app/styles.css'

mountApp(document.getElementById('app')!, {
  apiBaseUrl: '/api',
})

Mount @hookfish/api at the matching path in the deployment platform's request handler:

import { mountApi } from '@hookfish/api/app'

export default mountApi('/api', {
  exaApiKey: process.env.EXA_API_KEY,
})

A deployment owns only its HTML/document shell, static asset delivery, and platform adapter. Neither package requires a platform runtime, and the four shells in examples/ are working proofs of that — CI builds all of them on every pull request, so a change that breaks one runtime fails there rather than at deploy time.

Adding a source type

The frontend consumes the protocol-neutral ExecutableSource and Executable types in packages/app/src/lib/client-types.ts. Register source discovery/loading in packages/app/src/lib/source-adapters.ts. The launcher infers whether a pasted URL is MCP or OpenAPI. A source parser supplies executable names, badges, accent colors, JSON Schema inputs, targets, credentials, and UI labels. Homepage suggestions come from GET /api/registry/feed.

Register execution behavior with registerExecutableAdapter() in packages/app/src/lib/executable-adapters.ts. An adapter builds a serializable invocation, previews it, executes it, and can optionally export a code snippet. For example, an MCP adapter can map tools/resources/prompts to executables, use their names and input schemas directly, execute JSON-RPC through the MCP client and Hono RPC API, and export client/call setup code. The list, form, keyboard navigation, theming, and result viewer do not need protocol-specific changes.

MCP inspector

Pick an MCP server from the launcher or enter a Streamable HTTP endpoint. Hookfish uses the official MCP TypeScript client with automatic protocol negotiation:

  • MCP 2026-07-28 discovery, request metadata, MRTR, pagination, and subscriptions
  • legacy Streamable HTTP 2025-03-26 through 2025-11-25, including initialization, browser-held session IDs, GET event streams, and session termination
  • OAuth 2.1 authorization-code flows with PKCE, dynamic client registration, and Client ID Metadata Documents; registrations and tokens stay in browser storage
  • optional custom request headers (for example Authorization or X-API-Key), entered as name and value rows under Headers next to the launcher's Connect button (pasting .env, JSON, or Name: value text fills the rows), kept in browser storage, and sent only to the MCP server's origin; a custom Authorization replaces the OAuth token. OpenAPI sources send them on every request unless an operation parameter or auth scheme sets the same header
  • tools, resources, resource templates, and prompts rendered through the shared executable UI
  • request/response and notification traces plus capability metadata
  • sampling, elicitation, and roots requests with editable manual JSON responses
  • MCP Apps (io.modelcontextprotocol/ui): a tool that declares a ui:// resource renders its view in a sandboxed iframe after a call, with tool calls, resource reads, links, and messages from the view relayed through the host and recorded in the trace

The /api/mcp-proxy route streams requests and responses without retaining state. MCP connections, OAuth credentials, legacy session identifiers, and cached listings remain in the browser. The UI talks to the App component's apiBaseUrl through Hono RPC; the web shell mounts it at /api. Auto-generated OpenAPI is served at /api/openapi.json. Deprecated pre-Streamable-HTTP HTTP+SSE and stdio transports are intentionally not supported.

Suggestions and web search

GET /api/registry/feed returns the suggested sources in packages/api/src/registry.ts, keyed by category; the homepage renders each category as a pane in the listed order. Edit that file to change the suggestions.

GET /api/registry/search?q= asks Exa for remote MCP servers matching the query, using structured output: name, MCP URL, homepage, description, and how it authenticates (none, oauth, or headers with the header names it needs). It needs an EXA_API_KEY passed to mountApi as exaApiKey and answers 503 without one. The homepage only calls it when you ask it to search the web. Picking a search result fills the launcher with its URL and a blank row for each required header, for you to complete and confirm with Connect.

Build every workspace package:

pnpm build

The resulting CLI package runs the production app with Node—no Wrangler or Cloudflare runtime is required:

npm install --global hookfish
hookfish up --port 3000

hookfish with no command prints help. pnpm cli builds the workspace and runs that same bare command:

pnpm cli
pnpm cli up --host 127.0.0.1 --port 4000
pnpm cli update

If a newer version is on npm, commands print a warning to run hookfish update.

name and version in packages/cli/package.json are the npm identity. pnpm pack and pnpm publish run prepack, which builds the Node example into web/ so the tarball is self-contained:

pnpm --filter hookfish pack
pnpm --filter hookfish publish --access public

License

MIT. See LICENSE.

About

Resources

Stars

6 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages