Registering MCP services
Register MCP-backed services as explicit per-binding Buffaly services: ProtoScript owns the service boundary and configuration, while C# owns the MCP protocol work.
The model
The intended shape is one generic McpService type, one C# host bridge, and one concrete ProtoScript service instance for each configured MCP server.
Source model
McpService#FeedingFrenzy.ToListServerTools
McpService#GitHub.ToCallTool
Projected tool names
McpService_FeedingFrenzy_ToListServerTools
McpService_GitHub_ToCallTool
The host should initialize the binding before the first projected method call. Service methods should not require authors to call initialization manually.
Why MCP is per-binding
MCP servers usually differ by endpoint, transport, authentication, capability set, intended usage, filesystem roots, and other boundary data. Calling the wrong binding can be materially wrong.
Treat MCP like a Google Workspace account or SQL connection: the instance identity matters. Avoid a singleton unless there is truly one stable server with no meaningful environment or auth variation.
Authoritative split: ProtoScript versus C#
ProtoScript owns
- Service type and instance names.
- Binding fields and descriptions.
- Initialize() and user-facing To* signatures.
- Friendly wrapper methods once a binding is stable.
C# owns
- Transport and JSON-RPC session handling.
- MCP initialize handshake and request correlation.
- tools/list and tools/call.
- Connection caching, lifecycle state, and fail-fast binding errors.
Do not reimplement MCP protocol logic in ProtoScript. Keep ProtoScript authoring simple and let the C# bridge handle transport, handshake, and remote calls.
Recommended phase-one service surface
Start small. One real generic service is enough to prove the boundary before investing in generated or hand-authored wrappers.
After a binding is stable, add friendly wrappers such as ToSearchLeads(...), ToReadResource(...), or ToGetPrompt(...) for the remote actions users should rely on.
Binding options
Keep binding identity and credentials explicit. Put server-specific configuration on the service instance rather than passing free-form binding parameters into every method.
| Option | Purpose | Guidance |
|---|---|---|
| BindingKey | Stable machine-safe binding identity. | Use keys such as feedingfrenzy, github, or filesystem. |
| Description | Domain-specific summary of when to use the binding. | Avoid vague names like "Main server"; describe the real purpose. |
| ServerUrl | HTTP MCP endpoint. | Keep the URL explicit and authoritative. |
| BearerTokenEnvironmentVariable | Environment variable that supplies the bearer token. | Never hardcode sensitive tokens in ProtoScript. |
| TimeoutSeconds | Request timeout. | Set a practical timeout for the remote server. |
| Insecure | TLS bypass. | Use only for local or controlled test environments. |
| Stream | Stream session header pattern. | Set true when the server expects stream initialization notifications. |
Example: register a stable MCP binding
Declare a partial service instance for each stable server. The projected tools keep the instance identity visible to Buffaly and to operators.
Feeding Frenzy shape
- BindingKey: feedingfrenzy
- ServerUrl: remote MCP stream endpoint
- BearerTokenEnvironmentVariable: FF_MCP_TOKEN
- Stream: true
Validate remote tool discovery first.
Call one safe remote tool before adding wrappers.
Once validated, add hand-authored wrappers for important remote tools. Add a generator later only if the server surface becomes large.
Recommended workflow
- Create or reuse the generic McpService type.
- Declare one partial prototype for the server binding.
- Set BindingKey, endpoint, token environment variable, and transport options.
- Validate Initialize() and ToListServerTools() first.
- Validate ToCallTool(...) or ToCallTextTool(...) against one safe remote tool.
- Add friendly wrappers only after the binding is stable and useful.
Avoid hardcoded tokens
Use environment variables or protected secret storage. Do not place API keys in ProtoScript, docs, or examples.
Avoid overexposure
Start with generic calls and a curated wrapper set instead of exposing a large server surface too early.