Add Bearer auth, rewrite example server, add README
- lmcp.lua: optional Bearer token auth via conf file or explicit token - lmcp.lua: CORS Authorization header allowed - example_server.lua: rewritten with non-blocking shell, file ops, search - README.md: usage, auth config, Claude Code integration, tool examples Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -7,6 +7,19 @@ local json = require('json')
|
||||
local lmcp = {}
|
||||
lmcp.__index = lmcp
|
||||
|
||||
-- Read auth token from config file if present
|
||||
local function read_conf(path)
|
||||
local conf = {}
|
||||
local f = io.open(path, 'r')
|
||||
if not f then return conf end
|
||||
for line in f:lines() do
|
||||
local k, v = line:match('^%s*(%S+)%s*=%s*(.-)%s*$')
|
||||
if k and not k:match('^#') then conf[k] = v end
|
||||
end
|
||||
f:close()
|
||||
return conf
|
||||
end
|
||||
|
||||
-- Protocol constants
|
||||
local MCP_VERSION = "2025-03-26"
|
||||
local JSONRPC = "2.0"
|
||||
@@ -20,6 +33,13 @@ function lmcp.new(name, opts)
|
||||
self.port = opts.port or 8080
|
||||
self.tools = {}
|
||||
self._session_id = nil
|
||||
-- Auth: explicit opt > conf file > nil (no auth)
|
||||
if opts.auth_token then
|
||||
self._auth_token = opts.auth_token
|
||||
elseif opts.conf then
|
||||
local conf = read_conf(opts.conf)
|
||||
self._auth_token = conf['.godparticle']
|
||||
end
|
||||
return self
|
||||
end
|
||||
|
||||
@@ -178,6 +198,20 @@ function lmcp:serve_request(client)
|
||||
local path = req.path
|
||||
local accept = req.headers['accept'] or ''
|
||||
|
||||
-- Auth check (skip for OPTIONS, already handled above)
|
||||
if self._auth_token and req.method ~= 'OPTIONS' then
|
||||
local auth = req.headers['authorization'] or ''
|
||||
local token = auth:match('^Bearer%s+(.+)$')
|
||||
if token ~= self._auth_token then
|
||||
send_response(client, '401 Unauthorized',
|
||||
{ ['Content-Type'] = 'application/json',
|
||||
['WWW-Authenticate'] = 'Bearer' },
|
||||
'{"error":"unauthorized"}')
|
||||
client:close()
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
-- GET /mcp — SSE endpoint (for session establishment)
|
||||
if req.method == 'GET' and path:match('^/mcp') then
|
||||
-- SSE stream — send headers and keep alive briefly
|
||||
@@ -261,7 +295,7 @@ function lmcp:serve_request(client)
|
||||
send_response(client, '204 No Content', {
|
||||
['Access-Control-Allow-Origin'] = '*',
|
||||
['Access-Control-Allow-Methods'] = 'GET, POST, OPTIONS',
|
||||
['Access-Control-Allow-Headers'] = 'Content-Type, Accept',
|
||||
['Access-Control-Allow-Headers'] = 'Content-Type, Accept, Authorization',
|
||||
}, '')
|
||||
client:close()
|
||||
return
|
||||
@@ -307,3 +341,4 @@ function lmcp:run()
|
||||
end
|
||||
|
||||
return lmcp
|
||||
|
||||
|
||||
Reference in New Issue
Block a user