Documentation
¶
Overview ¶
Package mcp defines immutable, SDK-independent Model Context Protocol tools. It owns request-time capability availability, typed decoding and input validation; protocol adapters own transport, recovery, and emitted-output validation. Applications own authentication and resource authorization. Availability runs before decoding, not during construction, and does not replace resource checks inside handlers. Published tools can be shared across goroutines when policies, handlers and captured dependencies are concurrency-safe. Optional CLI presentation travels with ToolInfo, never in a second catalog. Example arguments are checked by typed decoding without executing input validation rules. Custom JSON unmarshaling methods can run during that construction-time check; this is not a guarantee that no user code executes. CLI roles describe diagnostic meaning, not command shortcuts or policy. ToolFailure explicitly approves structured diagnostics; ToolError approves text only. Adapters must keep policy failures private. Schemas are captured as owned JSON bytes; retained drafts and Describe reports cannot change them. Input schema violations wrap ErrInvalidInput, while operational Check errors preserve their cause under ErrInputSchema. Neither schema execution nor output encoding causes approve client-facing diagnostics, even when they wrap ToolError or ToolFailure. Typed integers and raw JSON fields retain number precision; decoding any uses float64 semantics.
Example:
tool := mcp.NewTool("status", func(ctx context.Context, in mcp.NoInput) (struct{}, error) {
return struct{}{}, nil
})
result, err := tool.Call(context.Background(), nil)
if err != nil {
panic(err)
}
fmt.Println(string(result)) // {}
Index ¶
- Variables
- func ToolError(message string) error
- func ToolErrorMessage(err error) (string, bool)
- func ToolFailure(code, message string, details jsontext.Value) error
- type Annotations
- type CLIInfo
- type Example
- type Failure
- type NoInput
- type Tool
- func (tool Tool) Available(ctx context.Context) (bool, error)
- func (tool Tool) Call(ctx context.Context, arguments jsontext.Value) (jsontext.Value, error)
- func (tool Tool) Describe() ToolInfo
- func (tool Tool) Err() error
- func (tool Tool) Name() string
- func (tool Tool) WithAvailability(policy func(context.Context) (bool, error)) Tool
- type ToolBuilder
- func (action *ToolBuilder[In, Out]) Available(policy func(context.Context) (bool, error)) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) CLI(group, operation string) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Cursor(cursor bool) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Description(text string) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Destructive(value bool) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Example(description, input string) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) GroupSummary(summary string) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Idempotent(value bool) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Input(schema validate.Schema[In]) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) OpenWorld(value bool) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Output(schema validate.Schema[Out]) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Positional(names ...string) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) ReadOnly(value bool) *ToolBuilder[In, Out]
- func (action *ToolBuilder[In, Out]) Role(role string) *ToolBuilder[In, Out]
- type ToolBuilderFunc
- type ToolInfo
Examples ¶
Constants ¶
This section is empty.
Variables ¶
var ErrAvailability = errors.New("mcp: availability evaluation failed")
ErrAvailability classifies a failed capability-policy evaluation. Its wrapped cause is for local diagnostics only, including causes marked with ToolError. Adapters must not expose policy diagnostics as client-safe handler messages.
Example: if errors.Is(err, mcp.ErrAvailability) { logger.Error("policy failed", "error", err) }
var ErrInputSchema = errors.New("mcp: input schema execution failed")
ErrInputSchema classifies an operational error from input schema execution, not a validation violation. Its original cause remains available locally; adapters must redact it even when it contains a ToolError or ToolFailure. A schema's Check should use validate.Fail for a caller-correctable violation.
Example:
_, err := tool.Call(ctx, arguments)
if errors.Is(err, mcp.ErrInputSchema) {
logger.Error("input schema failed", "error", err)
}
var ErrInvalidInput = errors.New("mcp: invalid input")
ErrInvalidInput classifies argument decoding and input-schema violations. Its wrapped cause remains available through errors.Is and errors.As. An operational Check failure instead wraps ErrInputSchema, not ErrInvalidInput.
Example: if errors.Is(err, mcp.ErrInvalidInput) { return err }
var ErrInvalidOutput = errors.New("mcp: invalid output")
ErrInvalidOutput classifies encoding and nonobject output failures. Output-schema enforcement belongs to the protocol adapter, not Call.
Example: if errors.Is(err, mcp.ErrInvalidOutput) { return err }
var ErrInvalidTool = errors.New("mcp: invalid tool")
ErrInvalidTool classifies malformed declarations, including a zero Tool. The wrapped cause describes the declaration defect; never expose it blindly.
Example: if errors.Is(tool.Err(), mcp.ErrInvalidTool) { return tool.Err() }
ErrUnavailable means the capability policy explicitly declined this request. No arguments were parsed and no handler ran. It does not classify policy execution failures, which instead wrap ErrAvailability.
Example: if errors.Is(err, mcp.ErrUnavailable) { return nil }
Functions ¶
func ToolError ¶
ToolError marks message as safe for the protocol adapter to disclose. Only explicitly approved, nonsecret text belongs here. Wrappers can retain internal context; ToolErrorMessage extracts only the original message. Core retains the complete message; an adapter may bound its wire length. This marker approves handler failures, not framework failures: availability, input decoding/schema execution, and output encoding classifications take precedence over markers nested inside their diagnostic causes.
Example: return mcp.ToolError("document is not available")
Example ¶
ExampleToolError exercises the error interface and approved-message extraction through Call. Private wrapping remains available locally, outside the marker.
package main
import (
"context"
"errors"
"fmt"
"arkoren.dev/core/mcp"
)
func main() {
cause := mcp.ToolError("document is not available")
tool := mcp.NewTool("lookup", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, fmt.Errorf("private lookup context: %w", cause)
})
_, err := tool.Call(context.Background(), nil)
message, ok := mcp.ToolErrorMessage(err)
fmt.Println(message, ok)
fmt.Println(errors.Is(err, cause))
fmt.Println(cause.Error())
}
Output: document is not available true true document is not available
func ToolErrorMessage ¶
ToolErrorMessage extracts approved text through ordinary error wrapping. It returns ("", false) for nil or unmarked errors. An approved empty message returns ("", true); adapters must not substitute the surrounding error text.
Example:
if message, ok := mcp.ToolErrorMessage(err); ok {
fmt.Println(message)
}
func ToolFailure ¶
ToolFailure marks code, message, and details as safe for clients. Details must be nil or a valid JSON object, and code must be nonempty; invalid declarations return an unmarked error that adapters redact. Bytes are copied immediately. Framework input/output failures and policy errors override this marker and remain private at the adapter boundary; only handler failures approve details.
Example:
return mcp.ToolFailure("unavailable", "capture is disabled", nil)
Types ¶
type Annotations ¶
type Annotations struct {
// ReadOnly reports that a tool does not mutate its environment.
// Example: if tool.Describe().Annotations.ReadOnly { fmt.Println("read only") }
ReadOnly bool
// Destructive reports possible destructive updates, unless ReadOnly applies.
// Example: if tool.Describe().Annotations.Destructive { fmt.Println("confirm") }
Destructive bool
// Idempotent reports repeatable effects for the same arguments.
// Example: fmt.Println(tool.Describe().Annotations.Idempotent)
Idempotent bool
// OpenWorld reports possible interaction with an unbounded external world.
// Example: fmt.Println(tool.Describe().Annotations.OpenWorld)
OpenWorld bool
}
Annotations are detached protocol hints, not authorization guarantees. Their conservative defaults are false, true, false, true respectively.
Example: hints := tool.Describe().Annotations; fmt.Println(hints.ReadOnly)
type CLIInfo ¶
type CLIInfo struct {
// Group is the CLI namespace; protocol names remain unchanged.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Group)
// }
Group string `json:"group"`
// Operation is the command within Group.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Operation)
// }
Operation string `json:"operation"`
// Summary optionally describes the group in help output.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Summary)
// }
Summary string `json:"summary,omitempty"`
// Role optionally labels a diagnostic meaning, such as identity or health.
// Empty means unspecified; a role neither authorizes use nor promises safety.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Role)
// }
Role string `json:"role,omitempty"`
// Cursor advertises follow through repeated bounded tool calls. The handler
// still owns cursor arguments and results; this field installs no behavior.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Cursor)
// }
Cursor bool `json:"cursor,omitempty"`
// Positional lists input property names in command-line argument order.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// fmt.Println(info.Positional)
// }
Positional []string `json:"positional,omitempty"`
// Examples contains detached, typed-decodable argument objects, or nil when
// none were declared. Reading a report does not repeat their decoding hooks.
//
// Example:
//
// if info := tool.Describe().CLI; info != nil {
// for _, sample := range info.Examples {
// fmt.Println(sample.Description, string(sample.Input))
// }
// }
Examples []Example `json:"examples,omitempty"`
}
CLIInfo reports optional CLI presentation in tools/list metadata. It does not rename the protocol tool or grant permissions. Cursor indicates ordinary bounded calls with cursor arguments, not a separate streaming transport. Roles label diagnostic meaning for clients, not command aliases or policy. Field examples below inspect a published Tool; its CLI report can be nil.
Example: if info := tool.Describe(); info.CLI != nil { fmt.Println(info.CLI.Operation) }
type Example ¶
type Example struct {
// Description explains the example's purpose, not its result.
// Example: fmt.Println(sample.Description)
Description string `json:"description"`
// Input is a JSON argument object compatible with the tool's Go input type.
// Example: fmt.Println(string(sample.Input))
Input jsontext.Value `json:"input"`
}
Example is a documented argument object, never an invocation. Input is copied when captured and reported; examples do not execute schema rules or handlers. Publication checks typed JSON decoding, which may invoke custom unmarshaling methods. Those application methods must be safe to run during construction.
Example: sample := mcp.Example{Description: "Recent", Input: jsontext.Value(`{"limit":10}`)}
type Failure ¶
type Failure struct {
// Code is the stable application error classification, such as unavailable.
// Example: fmt.Println(failure.Code)
Code string `json:"code"`
// Message is approved client-facing text, without internal context.
// Example: fmt.Println(failure.Message)
Message string `json:"message"`
// Details contains copied, explicitly approved fields, or nil when omitted.
// Example: fmt.Println(string(failure.Details))
Details jsontext.Value `json:"details,omitempty"`
}
Failure is an explicitly approved, detached client diagnostic. Details is an optional JSON object; neither wrapping context nor arbitrary errors are safe.
Example: if failure, ok := mcp.ToolFailureInfo(err); ok { fmt.Println(failure.Code) }
func ToolFailureInfo ¶
ToolFailureInfo extracts an approved record through ordinary error wrapping. Missing markers return the zero record and false. The returned details bytes are independent of both the original caller's input and previous reports.
Example:
if failure, ok := mcp.ToolFailureInfo(err); ok {
fmt.Println(failure.Code, failure.Message)
}
Example ¶
ExampleToolFailureInfo obtains approved structured diagnostics from a failed public call. The error interface contains only the approved message.
package main
import (
"context"
"encoding/json/jsontext"
"fmt"
"arkoren.dev/core/mcp"
)
func main() {
cause := mcp.ToolFailure("unavailable", "capture is disabled", jsontext.Value(`{"retry":false}`))
tool := mcp.NewTool("capture", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, fmt.Errorf("private capture context: %w", cause)
})
_, err := tool.Call(context.Background(), nil)
failure, ok := mcp.ToolFailureInfo(err)
if !ok {
fmt.Println(err)
return
}
fmt.Println(failure.Code, failure.Message)
fmt.Println(string(failure.Details))
fmt.Println(cause.Error())
}
Output: unavailable capture is disabled {"retry":false} capture is disabled
type NoInput ¶
type NoInput struct{}
NoInput describes an empty argument object, not JSON null. It is useful for tools that obtain all their data from dependencies.
Example:
tool := mcp.NewTool("status", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, nil
})
result, err := tool.Call(context.Background(), nil)
if err != nil {
return err
}
fmt.Println(string(result)) // {}
type Tool ¶
type Tool struct {
// contains filtered or unexported fields
}
Tool is an immutable typed handler erased for heterogeneous registration. Its zero value is invalid. Closure state is shared, not deep-copied.
Example:
tool := mcp.NewTool("status", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, nil
})
tools := []mcp.Tool{tool}
result, err := tools[0].Call(context.Background(), nil)
if err != nil {
return err
}
fmt.Println(string(result)) // {}
func NewTool ¶
func NewTool[In, Out any]( name string, fn func(context.Context, In) (Out, error), configure ...ToolBuilderFunc[In, Out], ) Tool
NewTool publishes an immutable tool without invoking fn or evaluating its availability policy. It performs no protocol I/O of its own. Construction callbacks and JSON methods used by schema export or CLI example decoding are application code and must be safe to run during construction. Names contain 1–128 ASCII letters, digits, underscores, dots or hyphens. Unspecified schemas are inferred; both must describe JSON objects. Invalid declarations retain their first error for Err and Call. Every callback runs once in order on one draft, including invalid declarations; nil callbacks and callback panics propagate. Nil fn is a declaration error, not a panic.
Example:
tool := mcp.NewTool("status", func(ctx context.Context, in mcp.NoInput) (struct{}, error) {
return struct{}{}, nil
}, func(action *mcp.ToolBuilder[mcp.NoInput, struct{}]) {
action.Description("Read status").ReadOnly(true)
})
if err := tool.Err(); err != nil {
panic(err)
}
Example ¶
ExampleNewTool demonstrates a transport-free call with inferred object schemas. Real adapters additionally validate the emitted result against OutputSchema.
package main
import (
"context"
"encoding/json/jsontext"
"fmt"
"arkoren.dev/core/mcp"
)
func main() {
type Document struct {
// ID is the document identifier echoed by this example.
ID string `json:"id"`
}
tool := mcp.NewTool("documents.echo", func(_ context.Context, input Document) (Document, error) {
return input, nil
}, func(action *mcp.ToolBuilder[Document, Document]) {
action.Description("Echo a document identifier").ReadOnly(true).OpenWorld(false)
})
result, err := tool.Call(context.Background(), jsontext.Value(`{"id":"doc-42"}`))
if err != nil {
panic(err)
}
fmt.Println(string(result))
}
Output: {"id":"doc-42"}
func (Tool) Available ¶
Available evaluates the request's capability policy without parsing input or invoking the handler. No policy means true. Invalid declarations and canceled contexts fail before policy execution. Policy errors preserve their cause and wrap ErrAvailability, even when that cause is a client-safe ToolError. Results are never cached; captures are shared and must be concurrency-safe. This does not replace resource authorization in the handler. Panics propagate.
Example:
offered, err := tool.Available(ctx)
if err != nil {
return err
}
if offered {
fmt.Println(tool.Name())
}
func (Tool) Call ¶
Call checks availability, decodes one object, parses input once, and invokes the handler once. A false policy returns ErrUnavailable before even decoding arguments; policy failures preserve ErrAvailability and their original cause. Nil arguments mean {}. Null, unknown struct members, and nonobjects fail with ErrInvalidInput. Canceled contexts prevent handler invocation. Output must encode as an object; output Parse is deliberately never called, so adapters must validate the emitted bytes against the published OutputSchema. Handler errors retain identity; panics propagate for adapter-level recovery. Operational input Check failures retain their cause under ErrInputSchema, not ErrInvalidInput; adapters must not disclose their error markers. Integer fields and jsontext.Value retain numeric precision; interface-held numbers follow encoding/json/v2's float64 decoding semantics. Use raw JSON fields instead of any when opaque numbers must remain exact.
Example:
result, err := tool.Call(context.Background(), jsontext.Value(`{}`))
if err != nil {
return err
}
fmt.Println(string(result))
func (Tool) Describe ¶
Describe returns metadata with independent copies of both schema documents. Check Err before registering; invalid tools may have incomplete metadata.
Example: info := tool.Describe(); fmt.Println(info.Description)
func (Tool) Err ¶
Err reports the first declaration defect, or nil for a usable tool. A zero Tool fails with ErrInvalidTool; callbacks still run on invalid drafts.
Example: if err := tool.Err(); err != nil { return err }
func (Tool) Name ¶
Name returns the exact declared identifier, even for an invalid declaration. It does not validate or normalize the name.
Example: fmt.Println(tool.Name())
func (Tool) WithAvailability ¶
WithAvailability returns a guarded copy without evaluating either policy. Both the existing policy and policy must permit each request, in that order; denial short-circuits. Nil policy records an invalid declaration. Captures remain shared and must be concurrency-safe; the original tool is unchanged.
Example:
guarded := tool.WithAvailability(func(ctx context.Context) (bool, error) {
return permissions.Allowed(ctx)
})
type ToolBuilder ¶
type ToolBuilder[In, Out any] struct { // contains filtered or unexported fields }
ToolBuilder holds construction-only edits for NewTool. Retaining it does not grant mutation of a published tool. Schemas remain shared immutable dependencies; functions and their captures stay application-owned and must support concurrent use. Schema bytes are copied during publication.
Example:
// status is the application's func(context.Context, mcp.NoInput) (Status, error).
tool := mcp.NewTool("status", status, func(action *mcp.ToolBuilder[mcp.NoInput, Status]) {
action.Description("Read current status").ReadOnly(true)
})
if err := tool.Err(); err != nil {
return err
}
func (*ToolBuilder[In, Out]) Available ¶
func (action *ToolBuilder[In, Out]) Available(policy func(context.Context) (bool, error)) *ToolBuilder[In, Out]
Available replaces the request-time capability policy. Omitting it offers the tool to everyone; explicitly passing nil records a sticky declaration error. The callback must be observational and concurrency-safe. It may run for both listing and calling with the same context, and its captures remain shared. Use it for roles or plans, not argument-dependent resource authorization: handlers must still authorize the resources they access. Panics propagate in core and are recovered by protocol adapters, like handler panics.
Example:
action.Available(func(ctx context.Context) (bool, error) {
return plans.CanExport(ctx)
})
func (*ToolBuilder[In, Out]) CLI ¶
func (action *ToolBuilder[In, Out]) CLI(group, operation string) *ToolBuilder[In, Out]
CLI sets the command namespace and operation without changing Tool.Name. Both must be nonempty protocol-style identifiers; final checks run after all callbacks, so settings can be declared in any order.
Example: action.CLI("telemetry", "traces")
func (*ToolBuilder[In, Out]) Cursor ¶
func (action *ToolBuilder[In, Out]) Cursor(cursor bool) *ToolBuilder[In, Out]
Cursor advertises follow by repeated bounded calls using ordinary arguments. It installs no handler, goroutine, or transport and defaults to false.
Example: action.Cursor(true)
func (*ToolBuilder[In, Out]) Description ¶
func (action *ToolBuilder[In, Out]) Description(text string) *ToolBuilder[In, Out]
Description replaces the human-readable description; empty is permitted. It returns the same construction draft and never edits a published Tool.
Example: action.Description("Look up a document by its identifier")
func (*ToolBuilder[In, Out]) Destructive ¶
func (action *ToolBuilder[In, Out]) Destructive(value bool) *ToolBuilder[In, Out]
Destructive sets the destructive-effects hint, which defaults to true. It is relevant when ReadOnly is false and does not trigger confirmation.
Example: action.Destructive(false)
func (*ToolBuilder[In, Out]) Example ¶
func (action *ToolBuilder[In, Out]) Example(description, input string) *ToolBuilder[In, Out]
Example appends a documented JSON argument object, retaining its own bytes. Final validation checks strict typed decoding only, never contextual schema rules or handlers. Examples are illustrative, not authorization guarantees. Custom JSON unmarshaling methods can run during NewTool publication, so they must be safe at construction time. Their errors become declaration errors. Availability guards calls, not these publication-time hooks. Custom methods remain responsible for their own decoding semantics and side effects.
Example: action.Example("Recent traces", `{"limit":10}`)
func (*ToolBuilder[In, Out]) GroupSummary ¶
func (action *ToolBuilder[In, Out]) GroupSummary(summary string) *ToolBuilder[In, Out]
GroupSummary replaces optional group help text. Empty removes the summary; it is presentation only and does not affect the tool description.
Example: action.GroupSummary("Captured telemetry")
func (*ToolBuilder[In, Out]) Idempotent ¶
func (action *ToolBuilder[In, Out]) Idempotent(value bool) *ToolBuilder[In, Out]
Idempotent sets the repeatable-effects hint, which defaults to false. It does not cause retries or deduplication during Call.
Example: action.Idempotent(true)
func (*ToolBuilder[In, Out]) Input ¶
func (action *ToolBuilder[In, Out]) Input(schema validate.Schema[In]) *ToolBuilder[In, Out]
Input replaces the typed input parser and snapshots its object schema. A nil, invalid or nonobject schema retains a sticky construction error. Parse runs once per permitted call. Rule violations wrap ErrInvalidInput; operational Check failures wrap ErrInputSchema and must remain private.
Example: action.Input(validate.Struct[Request]())
func (*ToolBuilder[In, Out]) OpenWorld ¶
func (action *ToolBuilder[In, Out]) OpenWorld(value bool) *ToolBuilder[In, Out]
OpenWorld sets the unbounded-external-interaction hint, defaulting to true. False describes a closed domain, not an enforced network restriction.
Example: action.OpenWorld(false)
func (*ToolBuilder[In, Out]) Output ¶
func (action *ToolBuilder[In, Out]) Output(schema validate.Schema[Out]) *ToolBuilder[In, Out]
Output replaces the published object schema without adding output parsing. Only JSON-expressible rules reach the adapter; normalizers and custom Check functions are not executed on output. Invalid settings retain a sticky error.
Example: action.Output(validate.Struct[Result]())
func (*ToolBuilder[In, Out]) Positional ¶
func (action *ToolBuilder[In, Out]) Positional(names ...string) *ToolBuilder[In, Out]
Positional replaces the ordered input property names and copies the slice. Names must be unique properties of the final input schema; empty clears them.
Example: action.Positional("id")
func (*ToolBuilder[In, Out]) ReadOnly ¶
func (action *ToolBuilder[In, Out]) ReadOnly(value bool) *ToolBuilder[In, Out]
ReadOnly sets the nonmutating hint, which defaults to false. Hints are descriptive and never enforce authorization or execution policy.
Example: action.ReadOnly(true)
func (*ToolBuilder[In, Out]) Role ¶
func (action *ToolBuilder[In, Out]) Role(role string) *ToolBuilder[In, Out]
Role labels optional diagnostic meaning for clients. Accepted values are identity, health, routes, traces, trace, logs, metrics, or empty to omit it. It does not rename the command, register a shortcut, authorize a call, or certify that the handler is read-only; those remain separate responsibilities.
Example: action.Role("health")
Example ¶
ExampleToolBuilder_Role publishes diagnostic meaning without adding a command alias or granting access. The independent availability policy still denies use.
package main
import (
"context"
"errors"
"fmt"
"arkoren.dev/core/mcp"
)
func main() {
tool := mcp.NewTool("service.health", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, nil
}, func(action *mcp.ToolBuilder[mcp.NoInput, mcp.NoInput]) {
action.CLI("service", "observe").Role("health")
action.Available(func(context.Context) (bool, error) {
return false, nil
})
})
if err := tool.Err(); err != nil {
fmt.Println(err)
return
}
fmt.Println(tool.Describe().CLI.Role)
_, err := tool.Call(context.Background(), nil)
fmt.Println(errors.Is(err, mcp.ErrUnavailable))
}
Output: health true
type ToolBuilderFunc ¶
type ToolBuilderFunc[In, Out any] = func(*ToolBuilder[In, Out])
ToolBuilderFunc configures a single construction draft synchronously. NewTool invokes callbacks once in order; nil entries panic as caller misuse.
Example:
policy := mcp.ToolBuilderFunc[mcp.NoInput, mcp.NoInput](
func(action *mcp.ToolBuilder[mcp.NoInput, mcp.NoInput]) {
action.ReadOnly(true)
},
)
tool := mcp.NewTool("status", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, nil
}, policy)
if err := tool.Err(); err != nil {
return err
}
fmt.Println(tool.Name())
Example ¶
ExampleToolBuilderFunc uses the public callback alias directly at publication. The resulting tool owns the captured settings, not the construction draft.
package main
import (
"context"
"fmt"
"arkoren.dev/core/mcp"
)
func main() {
policy := mcp.ToolBuilderFunc[mcp.NoInput, mcp.NoInput](
func(action *mcp.ToolBuilder[mcp.NoInput, mcp.NoInput]) {
action.ReadOnly(true)
},
)
tool := mcp.NewTool("status", func(context.Context, mcp.NoInput) (mcp.NoInput, error) {
return mcp.NoInput{}, nil
}, policy)
if err := tool.Err(); err != nil {
fmt.Println(err)
return
}
fmt.Println(tool.Name(), tool.Describe().Annotations.ReadOnly)
}
Output: status true
type ToolInfo ¶
type ToolInfo struct {
// Name is the case-sensitive protocol identifier.
// Example: fmt.Println(tool.Describe().Name)
Name string
// Description explains the tool to a client; it may be empty.
// Example: fmt.Println(tool.Describe().Description)
Description string
// InputSchema is an owned JSON Schema document with object root type.
// Example: fmt.Println(string(tool.Describe().InputSchema))
InputSchema jsontext.Value
// OutputSchema describes the emitted object; adapters validate emitted bytes.
// Example: fmt.Println(string(tool.Describe().OutputSchema))
OutputSchema jsontext.Value
// Annotations contains application-declared hints, not security policy.
// Example: fmt.Println(tool.Describe().Annotations.ReadOnly)
Annotations Annotations
// CLI is optional command presentation, independent of the protocol name.
// The returned pointer and its containers are detached on every Describe.
// Example: if info := tool.Describe(); info.CLI != nil { fmt.Println(info.CLI.Group) }
CLI *CLIInfo
}
ToolInfo is a detached report suitable for protocol registration. Editing its fields or schema bytes cannot change the published Tool.
Example: info := tool.Describe(); fmt.Println(info.Name, string(info.InputSchema))