Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Submit feedback
Contribute to GitLab
Sign in
Toggle navigation
I
imagej-elphel
Project
Project
Details
Activity
Releases
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
3
Issues
3
List
Board
Labels
Milestones
Wiki
Wiki
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Commits
Issue Boards
Open sidebar
Elphel
imagej-elphel
Commits
9c971dfc
Commit
9c971dfc
authored
Jan 27, 2026
by
Andrey Filippov
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Started MCP
parent
f3541d48
Changes
1
Hide whitespace changes
Inline
Side-by-side
Showing
1 changed file
with
238 additions
and
0 deletions
+238
-0
McpServer.java
src/main/java/com/elphel/imagej/mcp/McpServer.java
+238
-0
No files found.
src/main/java/com/elphel/imagej/mcp/McpServer.java
0 → 100644
View file @
9c971dfc
package
com
.
elphel
.
imagej
.
mcp
;
import
java.io.IOException
;
import
java.io.OutputStream
;
import
java.net.InetSocketAddress
;
import
java.net.URI
;
import
java.net.URLDecoder
;
import
java.nio.charset.StandardCharsets
;
import
java.util.HashMap
;
import
java.util.List
;
import
java.util.Map
;
import
com.elphel.imagej.correction.Eyesis_Correction
;
import
com.sun.net.httpserver.Headers
;
import
com.sun.net.httpserver.HttpExchange
;
import
com.sun.net.httpserver.HttpHandler
;
import
com.sun.net.httpserver.HttpServer
;
public
class
McpServer
{
private
static
McpServer
INSTANCE
;
private
final
Eyesis_Correction
owner
;
private
final
int
port
;
private
HttpServer
server
;
public
static
synchronized
McpServer
startIfNeeded
(
Eyesis_Correction
owner
,
int
port
)
{
if
(
INSTANCE
!=
null
)
{
return
INSTANCE
;
}
McpServer
instance
=
new
McpServer
(
owner
,
port
);
instance
.
start
();
INSTANCE
=
instance
;
return
instance
;
}
private
McpServer
(
Eyesis_Correction
owner
,
int
port
)
{
this
.
owner
=
owner
;
this
.
port
=
port
;
}
private
void
start
()
{
try
{
server
=
HttpServer
.
create
(
new
InetSocketAddress
(
"127.0.0.1"
,
port
),
0
);
}
catch
(
IOException
e
)
{
System
.
out
.
println
(
"MCP: failed to start HTTP server on port "
+
port
+
": "
+
e
.
getMessage
());
return
;
}
server
.
createContext
(
"/mcp/status"
,
new
StatusHandler
());
server
.
createContext
(
"/mcp/dialog"
,
new
DialogHandler
());
server
.
createContext
(
"/mcp/dialog/values"
,
new
DialogValuesHandler
());
server
.
createContext
(
"/mcp/button"
,
new
ButtonHandler
());
server
.
setExecutor
(
null
);
server
.
start
();
System
.
out
.
println
(
"MCP: server started on http://127.0.0.1:"
+
port
);
}
private
class
StatusHandler
implements
HttpHandler
{
@Override
public
void
handle
(
HttpExchange
exchange
)
throws
IOException
{
String
response
=
buildStatusJson
();
sendJson
(
exchange
,
200
,
response
);
}
}
private
class
DialogHandler
implements
HttpHandler
{
@Override
public
void
handle
(
HttpExchange
exchange
)
throws
IOException
{
String
response
=
buildDialogJson
();
sendJson
(
exchange
,
200
,
response
);
}
}
private
class
DialogValuesHandler
implements
HttpHandler
{
@Override
public
void
handle
(
HttpExchange
exchange
)
throws
IOException
{
if
(!
"POST"
.
equalsIgnoreCase
(
exchange
.
getRequestMethod
()))
{
sendJson
(
exchange
,
405
,
"{\"ok\":false,\"error\":\"POST required\"}"
);
return
;
}
Map
<
String
,
String
>
params
=
parseParams
(
exchange
);
String
label
=
params
.
get
(
"label"
);
String
value
=
params
.
get
(
"value"
);
if
(
label
==
null
)
{
sendJson
(
exchange
,
400
,
"{\"ok\":false,\"error\":\"Missing label\"}"
);
return
;
}
McpDialogRegistry
.
setValue
(
label
,
value
);
sendJson
(
exchange
,
200
,
"{\"ok\":true}"
);
}
}
private
class
ButtonHandler
implements
HttpHandler
{
@Override
public
void
handle
(
HttpExchange
exchange
)
throws
IOException
{
if
(!
"POST"
.
equalsIgnoreCase
(
exchange
.
getRequestMethod
()))
{
sendJson
(
exchange
,
405
,
"{\"ok\":false,\"error\":\"POST required\"}"
);
return
;
}
Map
<
String
,
String
>
params
=
parseParams
(
exchange
);
String
label
=
params
.
get
(
"label"
);
if
(
label
==
null
)
{
sendJson
(
exchange
,
400
,
"{\"ok\":false,\"error\":\"Missing label\"}"
);
return
;
}
owner
.
triggerCommand
(
label
);
sendJson
(
exchange
,
200
,
"{\"ok\":true}"
);
}
}
private
String
buildStatusJson
()
{
int
stopRequested
=
owner
.
getSyncStopRequested
();
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"{"
);
sb
.
append
(
"\"running\":"
).
append
(
owner
.
isSyncRunning
());
sb
.
append
(
",\"stopRequested\":"
).
append
(
stopRequested
);
sb
.
append
(
",\"buttonLabel\":\""
).
append
(
jsonEscape
(
owner
.
getSyncButtonLabel
())).
append
(
"\""
);
sb
.
append
(
"}"
);
return
sb
.
toString
();
}
private
String
buildDialogJson
()
{
McpDialogSession
session
=
McpDialogRegistry
.
getCurrent
();
if
(
session
==
null
)
{
return
"{\"ok\":true,\"dialog\":null}"
;
}
StringBuilder
sb
=
new
StringBuilder
();
sb
.
append
(
"{\"ok\":true,\"dialog\":{"
);
sb
.
append
(
"\"id\":\""
).
append
(
jsonEscape
(
session
.
getId
())).
append
(
"\","
);
sb
.
append
(
"\"title\":\""
).
append
(
jsonEscape
(
session
.
getTitle
())).
append
(
"\","
);
sb
.
append
(
"\"fields\":["
);
List
<
McpDialogField
>
fields
=
session
.
getFields
();
for
(
int
i
=
0
;
i
<
fields
.
size
();
i
++)
{
McpDialogField
f
=
fields
.
get
(
i
);
if
(
i
>
0
)
{
sb
.
append
(
","
);
}
sb
.
append
(
"{"
);
sb
.
append
(
"\"type\":\""
).
append
(
f
.
type
.
name
().
toLowerCase
()).
append
(
"\""
);
if
(
f
.
label
!=
null
)
{
sb
.
append
(
",\"label\":\""
).
append
(
jsonEscape
(
f
.
label
)).
append
(
"\""
);
}
if
(
f
.
tab
!=
null
)
{
sb
.
append
(
",\"tab\":\""
).
append
(
jsonEscape
(
f
.
tab
)).
append
(
"\""
);
}
if
(
f
.
tooltip
!=
null
)
{
sb
.
append
(
",\"tooltip\":\""
).
append
(
jsonEscape
(
f
.
tooltip
)).
append
(
"\""
);
}
if
(
f
.
units
!=
null
)
{
sb
.
append
(
",\"units\":\""
).
append
(
jsonEscape
(
f
.
units
)).
append
(
"\""
);
}
if
(
f
.
defaultValue
!=
null
)
{
sb
.
append
(
",\"default\":\""
).
append
(
jsonEscape
(
f
.
defaultValue
)).
append
(
"\""
);
}
if
(
f
.
choices
!=
null
)
{
sb
.
append
(
",\"choices\":["
);
for
(
int
c
=
0
;
c
<
f
.
choices
.
length
;
c
++)
{
if
(
c
>
0
)
{
sb
.
append
(
","
);
}
sb
.
append
(
"\""
).
append
(
jsonEscape
(
f
.
choices
[
c
])).
append
(
"\""
);
}
sb
.
append
(
"]"
);
}
sb
.
append
(
"}"
);
}
sb
.
append
(
"]}}"
);
return
sb
.
toString
();
}
private
static
void
sendJson
(
HttpExchange
exchange
,
int
code
,
String
body
)
throws
IOException
{
byte
[]
data
=
body
.
getBytes
(
StandardCharsets
.
UTF_8
);
Headers
headers
=
exchange
.
getResponseHeaders
();
headers
.
set
(
"Content-Type"
,
"application/json; charset=utf-8"
);
exchange
.
sendResponseHeaders
(
code
,
data
.
length
);
OutputStream
os
=
exchange
.
getResponseBody
();
os
.
write
(
data
);
os
.
close
();
}
private
static
Map
<
String
,
String
>
parseParams
(
HttpExchange
exchange
)
throws
IOException
{
Map
<
String
,
String
>
params
=
new
HashMap
<
String
,
String
>();
URI
uri
=
exchange
.
getRequestURI
();
if
(
uri
!=
null
&&
uri
.
getQuery
()
!=
null
)
{
parseQueryString
(
uri
.
getQuery
(),
params
);
}
if
(
"POST"
.
equalsIgnoreCase
(
exchange
.
getRequestMethod
()))
{
byte
[]
body
=
exchange
.
getRequestBody
().
readAllBytes
();
if
(
body
.
length
>
0
)
{
String
raw
=
new
String
(
body
,
StandardCharsets
.
UTF_8
);
parseQueryString
(
raw
,
params
);
}
}
return
params
;
}
private
static
void
parseQueryString
(
String
raw
,
Map
<
String
,
String
>
out
)
{
if
(
raw
==
null
||
raw
.
isEmpty
())
{
return
;
}
String
[]
pairs
=
raw
.
split
(
"&"
);
for
(
String
pair
:
pairs
)
{
if
(
pair
.
isEmpty
())
{
continue
;
}
int
idx
=
pair
.
indexOf
(
'='
);
String
key
=
idx
>=
0
?
pair
.
substring
(
0
,
idx
)
:
pair
;
String
value
=
idx
>=
0
?
pair
.
substring
(
idx
+
1
)
:
""
;
out
.
put
(
urlDecode
(
key
),
urlDecode
(
value
));
}
}
private
static
String
urlDecode
(
String
value
)
{
try
{
return
URLDecoder
.
decode
(
value
,
StandardCharsets
.
UTF_8
.
name
());
}
catch
(
Exception
e
)
{
return
value
;
}
}
private
static
String
jsonEscape
(
String
value
)
{
if
(
value
==
null
)
{
return
""
;
}
StringBuilder
sb
=
new
StringBuilder
(
value
.
length
()
+
10
);
for
(
int
i
=
0
;
i
<
value
.
length
();
i
++)
{
char
c
=
value
.
charAt
(
i
);
switch
(
c
)
{
case
'\\'
:
sb
.
append
(
"\\\\"
);
break
;
case
'"'
:
sb
.
append
(
"\\\""
);
break
;
case
'\n'
:
sb
.
append
(
"\\n"
);
break
;
case
'\r'
:
sb
.
append
(
"\\r"
);
break
;
case
'\t'
:
sb
.
append
(
"\\t"
);
break
;
default
:
sb
.
append
(
c
);
break
;
}
}
return
sb
.
toString
();
}
}
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment