Skip to Content

MCP Server

Any Golem application can be automatically exposed through MCP (Model Context Protocol). It requires no code — MCP is available for any agent automatically — but it must be enabled in the application manifest.

The MCP server uses port 9007 by default, separate from the HTTP API gateway on port 9006. The HTTP API gateway port also serves MCP deployments, chosen by the request’s host, so a deployment can expose both on one port. A domain holds either an HTTP API deployment or an MCP deployment, not both.

For self-hosted ingress, shared ports, and SSE keep-alives, see HTTP Gateway.

Enabling MCP

Add an mcp section to golem.yaml choosing which agents to deploy to which subdomains per environment:

mcp: deployments: local: - subdomain: mcp-demo # resolves to mcp-demo.localhost:9007 by default agents: CounterAgent: {}

After golem deploy, the MCP server is available at http://mcp-demo.localhost:9007/mcp using the streamable HTTP protocol.

Sessions

initialize starts a session. Its response carries the Mcp-Session-Id header, and the client sends that header with every later request of the session. The server keeps the session in a shared store, so the session continues when a server replica stops or restarts.

A session ends:

  • When it gets no request for the server’s mcp.session_idle_timeout, 5 minutes by default. A running tool call keeps its session open, so a call that takes longer than the idle timeout returns its result, and the session stays usable.
  • When the client sends DELETE to /mcp with the session ID. The server also cancels the running calls of the session.
  • When the server’s session store loses the session.

After a session ends, a request with its session ID gets 404 Not Found with the body Not Found: Session not found. The client must send initialize again to start a new session.

A session remembers the deployment that served its last initialize or list request (tools/list, resources/list, resources/templates/list or prompts/list). When the server serves a later tools/call, resources/read or prompts/get with another deployment, it sends a notifications/tools/list_changed notification, and the request fails with the JSON-RPC error -32602 tool set changed; list tools again. The client lists again and then repeats the request. Shortly after a deployment, a request can get this error once even when the client listed just before.

Exporting native tools

Native Golem tools can be exported without deploying an agent. A complete tools-only application declares the implementation, binds it to an owner component, and selects commands for the MCP deployment:

app: mcp-tools-demo environments: local: server: local components: my-app:file-tools: componentWasm: build/file-tools.wasm my-app:tool-owner: componentWasm: build/tool-owner.wasm tools: file-tools: {} tools: file-tools: component: my-app:file-tools mcp: deployments: local: - subdomain: mcp-demo tools: file-tools: ownerComponent: my-app:tool-owner include: - read-file - list **

The top-level declaration provisions the file-tools implementation. The components.my-app:tool-owner.tools binding authorizes each fresh owner to invoke it; an agent-only binding is insufficient. Component bindings also become defaults for real agents exported by that component, where agent bindings and presets can narrow or remove them. Use a separate component with no agents as the owner when those defaults are not wanted. The implementation component and owner component may be different, as above.

Finally, mcp.deployments.local[].tools.file-tools exports the selected executable commands. The deployment must contain at least one agent or tool. All security scheme references in it must agree; that scheme protects the whole domain.

For a remote release, put its exact release reference in the top-level declaration instead of component. The target environment must have a grant for that release. An application deployment plans and automatically reconciles grants derived from its remote declarations when the deploy actor has permission; separately managed grants are not removed.

Each call creates a fresh ephemeral owner using that component’s configuration, permissions, files, and tool bindings. It instantiates the deployed component, including its core initializers, but does not select an agent type or call an agent constructor. Tool and middleware bodies run through native invocation queueing and permission checks. Accepted execution is not restarted after owner loss, including interruption during core initialization. To invoke a tool on an existing real agent, use the native REST API or golem tool invoke --agent, not MCP.

Commands and schemas

Only commands with bodies become MCP tools; a parent with its own body is exported alongside its executable descendants. Names are lowercase, hyphens become underscores, and path segments are joined with underscores: file-tools read-file becomes file_tools_read_file. Aliases produce additional tools that invoke the same canonical command. Name collisions fail deployment.

Use either include or exclude, never both. Patterns are space-separated command paths without the tool prefix: * matches one segment and ** matches any depth. Selecting a canonical command includes its aliases; excluding it also removes its aliases.

Inputs use the human-readable JSON shapes advertised by tools/list, including defaults and repeated collections. Constraints are enforced before invocation and by native tool dispatch. Host-managed capabilities and nested typed streams cannot be exported. Standard annotations become readOnlyHint, destructiveHint, idempotentHint, and openWorldHint; these hints do not change permissions or execution semantics.

Finite stdin, stdout, and stderr

MCP buffers each byte stream, with a separate 16 MiB limit for stdin, stdout, and stderr. Exceeding any limit fails the MCP call; Golem does not truncate stream data. Declared stdin becomes an _stdin string argument, required only when the tool requires stdin. It contains UTF-8 text when every declared MIME type is textual, or base64 otherwise. Stdout and stderr are drained concurrently while stdin is being sent. Each becomes text, image, audio, or an embedded resource according to its declared MIME type. An output declared only with text/* MIME types must contain valid UTF-8 or the MCP call fails.

MCP has no native fd 2, so successful content lists stdout before stderr and tags every projected block with _meta["golem.cloud/tool-channel"] set to "stdout" or "stderr". Binary resources use distinct /stdout and /stderr URIs. This order does not imply cross-channel byte ordering, and stderr content alone does not set isError. On tool failure Golem still drains both outputs but omits their content, matching the existing stdout limitation.

Structured results are returned as structuredContent matching the advertised output schema, with a value wrapper when needed to produce a root object.

Invalid parameters, constraint violations, and invalid results produce JSON-RPC -32602 errors. Tool execution and custom errors produce isError: true. MCP does not expose native formatter selection, exit-code semantics, repetition modes, the alias graph, or middleware composition; middleware still runs normally.

Listings and calls are tied to a deployment definition. After a deployment changes, an existing session may receive a tool-list-changed notification and must list tools again before calling. Each MCP call has a fresh native idempotency key; repeating a JSON-RPC request ID is not a durable execution retry.

Automatic Mapping

Agent methods are automatically mapped to MCP entities based on the agent type and method signature:

AgentMethodMCP entity
SingletonNo parametersResource
Non-singletonNo parametersResource template
AnyHas parametersTool

Singleton agents (those with no constructor parameters) expose parameterless methods as MCP resources — static pieces of data that can be read by the client. Non-singleton agents expose parameterless methods as MCP resource templates, where the constructor parameters become template variables. Any method that takes parameters is exposed as an MCP tool that can be invoked by the AI model.

Method metadata

Method descriptions and prompt hints become MCP tool and resource metadata, helping AI models understand what each tool does and how to use it. TypeScript defines them with the description and promptHint properties of method(...); other SDKs use the corresponding language-specific annotations.

import { z } from 'zod'; import { defineAgent, method } from '@golemcloud/golem-ts-sdk'; export const CounterAgent = defineAgent({ name: 'CounterAgent', id: { name: z.string() }, methods: { incrementBy: method({ input: { value: z.number() }, returns: z.number(), description: "Add a value to the current counter", promptHint: "Increments the counter by the given value and returns the new count", }), }, }); export const CounterAgentImpl = CounterAgent.implement({ init: () => ({ count: 0 }), methods: { incrementBy({ value }) { this.count += value; return this.count; }, }, });

Security

MCP servers can be secured with OAuth2. Use the CLI to create a security scheme:

golem api security-scheme create mcp-oauth \ --provider-type custom \ --custom-provider-name "mock-oauth2" \ --custom-issuer-url "http://localhost:9099/golem" \ --client-id "golem-mcp-client" \ --client-secret "golem-mcp-secret" \ --scope openid --scope email --scope profile \ --redirect-url "http://mcp-demo.localhost:9007/mcp/oauth/callback"

Once the security scheme is created, reference it in the golem.yaml deployment to protect your MCP endpoints.

Special Data Types

Golem provides special data types that map naturally to MCP content types:

  • UnstructuredBinary — for binary blobs and images, mapped to MCP binary content
  • UnstructuredText — for text with MIME types, mapped to MCP text content
  • Multipart — for multipart data, supporting mixed content in a single response

These types allow agents to exchange rich content with MCP clients beyond simple JSON values.

MoonBit exposes @schema.UnstructuredText[L] and @schema.UnstructuredBinary[M] from golemcloud/golem_sdk/schema, including inline and URL constructors. See unstructured text and binary values for construction examples. For mixed content, MoonBit uses @multimodal.Multimodal[T] and #derive.multimodal; it does not expose a separate public Multipart wrapper. The removed #derive.text_languages and #derive.mime_types annotations are not available.

Testing with MCP Inspector

You can test your MCP server using the MCP Inspector :

npx @modelcontextprotocol/inspector node build/index.js

The inspector provides a web UI to browse available tools and resources, invoke them, and inspect the responses — useful for verifying your agent’s MCP interface before connecting it to an AI model.

Importing tools from another MCP server

mcp.deployments exposes Golem agents to external clients. In the opposite direction, mcp.imports makes an external MCP server’s tools available to Golem agents through the ordinary tool APIs and generated tool clients.

For example, import the page-fetching tool from the public Microsoft Learn MCP server . This server does not require authentication. Declare the import for your environment:

mcp: imports: local: - name: microsoft-learn url: https://learn.microsoft.com/api/mcp prefix: learn include: ["microsoft-docs-fetch"]

Upstream names are sanitized to kebab-case before filtering. The prefix is joined with -, so microsoft_docs_fetch becomes learn-microsoft-docs-fetch. Use either include or exclude, not both. Application tools and the selected environment’s ambient native tools take precedence over imports; among imports, earlier entries win. Imports do not need top-level tools declarations. The name of a default tool, such as bash, is reserved: when the deployment has a tool with that name, a deployment is refused if an import lists a tool with the same name at deploy time. If the tools of the imports cannot be read at deploy time, the deployment is refused when an import can supply that name; an import with a prefix, or with a filter that leaves the name out, cannot. The optional version selects the MCP protocol revision, not a tool release. The supported import protocol is currently 2026-07-28 Streamable HTTP.

Generate typed clients

Follow Generate typed clients for existing tools for the generated package setup. Client generation selects a schema; a deployed binding and the calling agent’s permissions authorize invocation.

Add the projected name to a component’s tool dependencies, using the component’s normal language template:

components: my-app:consumer: # Keep the component's existing dir, templates and build settings. dependencies: tools: [learn-microsoft-docs-fetch] tools: learn-microsoft-docs-fetch: {}

The dependency automatically selects client generation in the consumer’s language; no bridge.<language>.internal.tools entry is needed. The separate tools binding exposes the import for invocation. A dependency does not add a binding, and a binding alone does not generate a client. Configure the calling agent’s runtime permissions separately.

golem build resolves the current manifest’s imports before automatically generating the dependency clients. It does not use an older deployment’s declarations. Rust, TypeScript, Effect, Scala, MoonBit and Go use their existing tool-client APIs. Unavailable metadata fails client generation rather than silently generating stale types. A changed projection causes regeneration on the next build. The generated client calls the projected canonical Golem name through tool-rpc; the upstream MCP name remains runtime metadata. Generated sources contain no MCP endpoint, direct MCP transport, or authentication credentials.

The result has a structured field and a content variant. If the upstream tool declares outputSchema, structured has that schema’s generated type. Otherwise it is optional JSON text. A single text, image or audio block uses stdout; its content descriptor carries the MIME type and annotations. Multiple blocks and resources stay ordered in content, without stdout. Embedded binary fields use the SDK’s unstructured-binary type. Empty content and an empty stdout payload are distinct cases. Use the SDK’s collection helper to consume stdout and the result concurrently.

Call the imported tool

These helpers fetch the Azure Functions overview. Call the helper from an agent method after golem build generates the client. Add the generated package to your language’s dependencies as described in the client generation guide above. The empty map supplies additional input properties; this tool needs only url. Each example drains the output streams and returns the text from stdout.

import { LearnMicrosoftDocsFetchClient } from "learn-microsoft-docs-fetch-tool-guest-client"; async function fetchDocumentation(): Promise<string> { const invocation = LearnMicrosoftDocsFetchClient.newClient() .learn_microsoft_docs_fetch( "https://learn.microsoft.com/en-us/azure/azure-functions/functions-overview", new Map(), ); const collected = await invocation.collect(); if (collected.result.status === "rejected") throw collected.result.reason; if (collected.stdout.status === "rejected") throw collected.stdout.reason; if (collected.stderr.status === "rejected") throw collected.stderr.reason; return new TextDecoder().decode(collected.stdout.value); }

Authorize an import before deployment

For OAuth, reference a configured security scheme using securityScheme on the import. Alternatively, configure auth.bearer or auth.basic (user and password); inline authentication and securityScheme are mutually exclusive. Keep credentials out of committed manifests and use environment-variable templates for secret values.

The calling agent needs network permission for the MCP endpoint and, when OAuth tokens must be refreshed, the provider’s token endpoint. Discovery and refresh consume the environment owner’s HTTP quota.

OAuth-protected imports need consent before generating typed clients. The environment and security scheme must already exist, but the import need not be deployed. For an OAuth-protected import named catalog (unlike the public Microsoft Learn import above):

golem api mcp-import authorize catalog --manifest # Open the authorization URL, then copy the full callback URL. golem api mcp-import complete catalog --manifest '<callback-url>' golem api mcp-import status catalog --manifest

The optional name is CLI metadata and must match [a-z][a-z0-9_-]*, unique within the environment. Without a name, use the exact resolved full URL as the selector. If several imports share a URL, add distinct names and select one. Names do not change tool precedence, reach the server, or isolate OAuth grants. URL selection ignores declarations whose URLs cannot be rendered when exactly one successfully rendered URL matches. If none matches, the CLI reports which declarations could not be rendered.

Command output uses the selected declaration’s name, even when selected by URL. For unnamed imports, structured output contains "import": null and human output uses “Unnamed MCP import”. The CLI does not echo the import URL because it can contain credentials. For unnamed OAuth imports, replace '<import-url>' in the printed completion command with the original exact URL. Keep the printed --revision or --manifest option.

These commands do not add imports: declaring an OAuth-protected upstream does not authorize it. Keep the import configuration and its rendered environment variables unchanged between authorization and completion: consent is bound to that exact target. The CLI does not start a callback listener. These operations require permission to update the security scheme. Golem stores the grant under the environment owner’s account; the provider identity is the one used during consent.

For a deployed import, omit --manifest and use the revision printed by authorize when completing consent. --manifest and --revision are mutually exclusive. To revoke a grant, use golem api mcp-import disconnect catalog --manifest, or select a deployed revision instead. Redeploying alone does not grant consent.

Inspect and refresh deployed definitions

golem api mcp-import tools microsoft-learn --revision 42 golem api mcp-import refresh microsoft-learn --revision 42 # A name is not required for an import with a unique URL: golem api mcp-import tools 'https://learn.microsoft.com/api/mcp' --revision 42

Deployed commands require a local manifest and permission to view the deployment summary. The CLI matches the selected import’s complete configuration against the selected revision, so local reordering does not change the target. A missing match requires the declaration and environment values for that revision, or deployment of the changed configuration. Identical deployed configurations cannot be selected unambiguously even with distinct CLI names; use distinct configurations.

Omitting --revision selects the environment’s current deployment. Inspection shows that import’s projected definitions before native and earlier-import precedence, plus reasons for excluded definitions. Explicit refresh reports upstream failures; ordinary reads can retain usable last-successful metadata. Cache misses fetch on demand, so periodic refresh is only an optimization.

Deployment attempts best-effort discovery using the operator’s permissions. It reports excluded definitions, collisions and unavailable metadata as warnings, without blocking a valid deployment or dropping its import declarations. These warnings do not guarantee that a later agent call has the necessary permissions or that the upstream definitions remain unchanged.

Durable invocations select imports from the deployment associated with the running agent and record the dynamic definition used. Completed tool calls replay their recorded results without contacting the MCP server or OAuth provider. An interrupted, incomplete call may be retried with the same derived idempotency key; avoiding duplicate upstream effects still requires the provider to honor that key.