@flink-app/mcp-plugin
v2.0.0-alpha.121
Published
Flink plugin that exposes Flink AI tools over MCP (Model Context Protocol) via Streamable HTTP
Readme
@flink-app/mcp-plugin
Expose Flink AI tools as an MCP server so MCP clients — Claude Code, Claude Desktop, Cursor, custom agents — can call them over HTTP.
- Uses the Streamable HTTP transport (stateless, load-balancer friendly), mounted on the Flink app's own Express server.
- Tools are opt-in per tool via
mcp: true. - Every call runs inside Flink's request context: the authenticated user and permissions flow into tools exactly as for agents, and the tool's declarative
permissionsare enforced.
Install
pnpm add @flink-app/mcp-pluginUsage
import { FlinkApp } from "@flink-app/flink";
import { mcpPlugin } from "@flink-app/mcp-plugin";
const app = await new FlinkApp<AppCtx>({
name: "My app",
auth: jwtAuthPlugin({ ... }),
plugins: [
mcpPlugin({
instructions: "Tools for looking up cars in the demo inventory.",
}),
],
}).start();Mark the tools you want to expose:
export const Tool: FlinkToolProps = {
id: "search-cars-by-brand",
description: "Search for cars by brand name.",
inputSchema: z.object({ brand: z.string() }),
permissions: ["car:read"],
mcp: true, // 👈 visible over MCP
};Tools without mcp: true stay internal to your agents.
Connecting from Claude Code
claude mcp add --transport http my-flink-app https://api.example.com/mcp \
--header "Authorization: Bearer <token>"Options
| Option | Default | Description |
| -------------- | ----------------------- | --------------------------------------------------------------------------------------------- |
| path | /mcp | Endpoint path. |
| name | app name | Server name reported to clients. |
| version | 1.0.0 | Server version reported to clients. |
| instructions | — | Short description sent to clients on initialize, shown to the model with the tool list. |
| auth | app auth plugin | See Authentication. |
| filter | tool.mcp === true | Custom rule for which tools to expose. Replaces the default. |
| debug | false | Log each tool call. |
Authentication
| auth value | Behaviour |
| -------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------- |
| omitted | Uses the app's auth plugin (authenticateRequest with no required permissions — a valid token is enough). Without an auth plugin the endpoint is open and a warning is logged. |
| { permissions: [...] } | Uses the app's auth plugin and requires these permissions to reach the endpoint at all. |
| false | Explicitly open, no warning. |
| async (req, app) => ... | Custom. Return { user, permissions }, false (401), or a FlinkResponse to shape the rejection. |
Rejected requests get a 401 with WWW-Authenticate: Bearer (or the status of the returned FlinkResponse). Per-tool permissions are always enforced on top, using the permissions resolved by authentication.
How tool results map to MCP
| Flink ToolResult | MCP CallToolResult |
| ------------------------------------- | ------------------------------------------------------------------------ |
| { success: true, data: object } | content: [text(JSON)] + structuredContent: data |
| { success: true, data: primitive } | content: [text(data)] |
| { success: false, error, code } | isError: true, content: [text("CODE: error")] |
| thrown Flink error (e.g. forbidden) | isError: true, content: [text("PERMISSION_DENIED: ...")] |
Input schemas come from inputSchema (Zod 4), inputJsonSchema, or the compiler-generated schema — same priority as for agents. outputSchema is advertised when it describes an object.
Plugin context
ctx.plugins.mcp.listTools(); // McpToolDefinition[] currently exposed
ctx.plugins.mcp.path; // "/mcp"Notes
- Stateless transport:
GET/DELETEon the endpoint return405(no sessions, no server-initiated notifications). This is what Claude Code and other current clients expect. - Tool descriptions become the model's only documentation — make them explicit about arguments and side effects.
- Long-running tools must finish within the client's request timeout.
